在Linux下使用Hook修改GPS接口数据 郝伟 2021/01/10 [TOC]
1. 1 目的
GPS定位在系统中是通过硬件芯片完成。操作系统通过驱动程序实现对硬件资源进行管理。当软件需要GPS信息时,只需向系统申请调用相应的API接口即可。在本验证中,我们的目标是在程序调用了GPS的API接口时,将返回的数据进行一定的修改,从而让应用程序无法获得真实的GPS信息。
2. 2 实现原理
我们的目标在修改GPS返回的数据,所以需要通过一定的机制,能够修改应用程序调用系统API返回的数据。为了实现这个目标,可以利用Hook的机制,在应用程序与系统接口后再加上一层Hook,从而实现数据的修改目标,这里我们可以利用系统预加载模块。在Linux系统中,ld-linux.so.2
是一个动态库加载器(Dynamic Loader, DL),用于在系统加时自动搜索系统参数并加载相应的共享库。对于GPS模块来说,需要由相应的库函数提供相应的地理信息读写功能。所以系统在启动后,加载这个库,从而实现其他程序对此库的调用,这过程就是系统启动的 预启动(preload)
过程。
为了让系统知道需要将哪些链接库引入到系统预启动过程,我们可以通过可选变量 LD_PRELOAD
进行配置。这个变量实际是一组文件索引,可以包含多个指向共享链接库文件的路径。加载器会先于 C 语言运行库之前载入LD_PRELOAD
指定的共享链接库,从而提前指定链接到哪个.so文件,完成所需要的预加载过程。
基于这个原理,我们在系统加载时,利用此功能编写我们的GPS调用函数,然后进行修改,并返回修改后的数据。从而以Hook原理实现对GPS模块数据的修改。
(注:如果下图显示不正常,请参见这里)
正常的GPS数据读取过程 在正常的没有加入Hook之前,GPS的调用系统接口的过程如下所示:
@startuml autonumber 1 1 "<b>[0]" 应用程序 -> 操作系统: 调用GPS接口 操作系统 --> GPS库: 直接在动态库中找到原GPS库的并调用相应接口 操作系统 <- GPS库: 返回真实的GPS数据 应用程序 <- 操作系统: 返回真实的GPS数据 @enduml
通过Hook机制修改GPS数据读取过程 在加入了Hook机制以后,多了一层自定义库的Hook层,从而实现数据的修改,过程如下所示:
@startuml autonumber 1 1 "<b>[0]" 应用程序 -> 操作系统: 调用 GPS 接口 操作系统 -> 自定义库进行Hook: 发现同签名预加载库调用自定义的GPS接口 自定义库进行Hook -[#0000FF]> GPS库: 调用真实的GPS接口 GPS库 -[#0000FF]> 自定义库进行Hook: 返回真实的GPS数据 自定义库进行Hook -[#red]> 自定义库进行Hook: 修改数据 note right of 自定义库进行Hook: 第[5]步是重要步骤,数据在此完成修改 操作系统 <- 自定义库进行Hook: 返回修改后的数据 应用程序 <- 操作系统: 返回修改后的GPS数据 @enduml
3. 3 验证过程
3.1. 3.1 硬件设备
本实验在开发板 fl2440上进行,使用的GPS开发模块是A7版。
3.2. 3.2 GPS调用接口
3.2.1. 3.2.1 GPS信息结构体
#ifndef __GPSInfo_H__
#define __GPSInfo_H__
typedef unsigned int UINT;
typedef int BYTE;
typedef struct __grpinfo__
{
UINT date; /* 日期 */
UINT time; /* 时间 */
char mode; /* 模式 */
char state; /* 状态 */
float longitude; /* 经度 */
float latitude; /* 纬度 */
float speed; /* 速度 */
float direction; /* 方向 */
float declination; /* 倾角 */
}GPSINFO;
#endif
3.2.2. 3.2.2 GPS读取接口
GPS默认的数据格式为一串格式化的字符串,而GPS的读取函数接口定义如下所示:
// 创建指针,返回GPSINFO对象指针,
// id: 所要读取的GPS模块编号
// gps_info: 返回的数据指针
// 返回值:结果0表示失败,其他值表示成功。
int gps_read(int id, GPSINFO *gps_info);
3.2.3. 3.2.3 输出函数
为了方便演示,定义打印输出函数如下所示:
int print_gps (GPSINFO *gps_info)
{
int lon_d = (int)(gps_info->latitude / 100);
int lon_c = (int)(gps_info->latitude - (lon_d * 100));
int lon_s = (int)((lon_c - lon_c) * 60.0));
int lat_d = (int)(gps_info->longitude / 100);
int lat_c = (int)(gps_info->longitude - (lat_d * 100));
int lat_s = (int)(((gps_info->longitude - lat_d * 100) - lat_d * 100) * 60.0);
printf("========= GPS定位读取接口 =============\n");
printf("= 纬度: 北纬:%3d度%2d分%2d秒 =\n", lon_d, lon_c, lon_s);
printf("= 经度: 东经:%3d度%2d分%2d秒 =\n", lat_d, lat_c, lat_s);
printf("======================================\n");
return 0;
}
3.3. 3.3 自定义.so库
以上是自定义的C库,用于伪造的GPS的调用的 preload_gps.so
文件。
#include <string.h>
#include <dlfcn.h>
#include "gpsinfo.h"
// 定义调用接口函数指针
typedef int(*Gps_read)(int, GPSINFO*);
// 自定义的gps_read函数,如果使用了预加载,那么系统调用时时会优先调用此函数而不是默认函数
// 但是在本函数内,由于已经是预加载函数,所以不会再进行一次预加载判断。
int gps_read(const int id, GPSINFO* gps_info)
{
// 使用RTLD_LAZY进行延迟加载,即对动态库中存在的未定义的变量不执行变量的地址解析
static void* handle = dlopen("gps.so", RTLD_LAZY);
// 获得默认接口的地址指针
static Gps_read org_gps = (Gps_read)dlsym(handle, "gps_read");
// 以下内容对应图上第[3] - [5] 步
// 如果解析失败,则返回0,表示未读取到gps数据。
if(!org_gps)
return 0;
// 调用原始的GPS接口,获得GPS数据。
if(!org_gps(id, &ginfo))
return 0;
// 修改GPS数据数据,对应图上的第[5]步,将公司地址修改为地中海岛国塞普鲁斯的位置
ginfo->latitude=25.0114;
ginfo->longtitude=35.0542;
// 返回1表示数据读取成功
return 1;
}
编译代码:
$ gcc -fPIC gps.c -shared -o preload_gps.so -ldl
4. 4 验证结果
程序运行以后,结果如下:
正常调用的情况
# 华云安公司地址:116.2549, 40.0793 ========= GPS定位读取接口 ======= = 纬度: 北纬: 40度 4分24秒 = = 经度: 东经:116度14分56秒 = ================================
使用Hook加载的情况
# 塞普鲁斯(地中海岛国): 25.0114, 35.0542 ========= GPS定位读取接口 ======= = 纬度: 北纬: 25度 0分41秒 = = 经度: 东经: 35度 3分15秒 = ================================
5. 5 结论
通过使用预加载,可以对系统的GPS模块的库进行Hook操作,从而在调用后修改GPS数据达到预期的效果。
6. 参考资料
[1] 经纬度换算,https://www.fcc.gov/media/radio/dms-decimal [2] GPS定位C语言简介,https://blog.csdn.net/zouleideboke/article/details/73521122 [3] Linux下Hook方式汇总, https://xz.aliyun.com/t/6961 [4] Linux逆向之hook&注入, https://xz.aliyun.com/t/6883#toc-1 [5] Linux预加载ld-linux.so.2, https://www.cnblogs.com/kelamoyujuzhen/p/9823272.html [6] PlantUML 活动图画法, https://plantuml.com/zh/activity-diagram-beta [7] PlantUML 流程图画法, https://plantuml.com/zh/sequence-diagram [8] FL2440开发板的介绍和烧录, https://blog.csdn.net/a4729821/article/details/75570690 [9] 百度经纬度查询,http://api.map.baidu.com/lbsapi/getpoint/?qq-pf-to=pcqq.c2c