手机虚拟定位 可以在家里钉钉打卡

手机虚拟定位 可以在家里钉钉打卡 现在很多的应用都是基于位置服务的,而且把位置服务作为重要的参考标准,其实这是很不安全的,位置信息的数据未经过任何加密,而且是从我们的手机中发送出去的,所以是可以修改的。这一期我们来探讨一下如何修改手机中的定位信息。太基础的原理我就不多说了,可以参考前几期文章。
        先整理一下思路,Android在开发者模式下有一个"允许模拟位置选项",它是location service加载 MOCK location provider 实现的,通过这种线程注入的方式修改GPS信息是hacker们最喜欢的方式,但现在很多应用已经可以检测到这种注入方式而被屏蔽掉,也就是说如果我们只在APP层面上想解决方法总是有被检测出来的可能。那我们就得把问题往深了想,通过修改最底层的GPS数据来欺骗APP,在Framework层面上没有任何修改迹象,这样基于APP层面的检测机制就拿我们没有任何办法。
        思路确定后我们来探讨实践路线,首先我们要建立一个管道,让我们想要定位的GPS数据提交到Android操作系统的最底层,也就是Linux Kernel层面;然后我们要修改 GPS的 location report 机制,让它从内核中提取到我们的数据,然后逐层上报到APP层。有点明修栈道暗度陈仓的感觉。


        总体来说分成两部实现:1.建立到系统内核的数据管道;2.修改GPS上报机制。
        这次实验使用的是闲置的神乐三代手机,编译源码采用CyanogenMod-13
        因为Android系统从内核态到APP层要经过很多的层次,所以对于建立数据管道的步骤比较繁琐,我这里分成了5个步骤,对应5个层面来实现,每一步分别对应Android的 Kernel driver,HAL,JNI,Framework,Application。
下面描述一下实践步骤:
=============分割线1==============
第一步,修改Kernel driver
进入 kernel/xiaomi/cancro/drivers 目录下,新建vp.h文件

  1. #ifndef _VP_ANDROID_H_

  2.  

    #define _VP_ANDROID_H_

  3.  

  4.  

    #include <linux/cdev.h>

  5.  

    #include <linux/semaphore.h>

  6.  

  7.  

    #define VP_DEVICE_NODE_NAME "vp"

  8.  

    #define VP_DEVICE_FILE_NAME "vp"

  9.  

    #define VP_DEVICE_PROC_NAME "vp"

  10.  

    #define VP_DEVICE_CLASS_NAME "vp"

  11.  

  12.  

    typedef struct {

  13.  

    int toggle;

  14.  

    double virtual_latitude;

  15.  

    double virtual_longitude;

  16.  

    } VirtualPosition;

  17.  

  18.  

  19.  

  20.  

  21.  

    struct vp_android_dev {

  22.  

    int lamp;

  23.  

    VirtualPosition val;

  24.  

    struct semaphore sem;

  25.  

    struct cdev dev;

  26.  

    };

  27.  

    #endif

新建vp.c文件

  1. /*******************************************

  2.  

    *include file and define functions

  3.  

    *******************************************/

  4.  

    #include <linux/init.h>

  5.  

    #include <linux/module.h>

  6.  

    #include <linux/types.h>

  7.  

    #include <linux/fs.h>

  8.  

    #include <linux/proc_fs.h>

  9.  

    #include <linux/device.h>

  10.  

    #include <asm/uaccess.h>

  11.  

  12.  

    #include "vp.h"

  13.  

  14.  

    /*主设备和从设备号变量*/

  15.  

    static int vp_major = 0;

  16.  

    static int vp_minor = 0;

  17.  

  18.  

    /*设备类别和设备变量*/

  19.  

    static struct class* vp_class = NULL;

  20.  

    static struct vp_android_dev* vp_dev = NULL;

  21.  

  22.  

    /*传统的设备文件操作方法*/

  23.  

    static int vp_open(struct inode* inode, struct file* filp);

  24.  

    static int vp_release(struct inode* inode, struct file* filp);

  25.  

    static ssize_t vp_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);

  26.  

    static ssize_t vp_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);

  27.  

  28.  

    /*设备文件操作方法表*/

  29.  

    static struct file_operations vp_fops = {

  30.  

    .owner = THIS_MODULE,

  31.  

    .open = vp_open,

  32.  

    .release = vp_release,

  33.  

    .read = vp_read,

  34.  

    .write = vp_write,

  35.  

    };

  36.  

  37.  

    /*访问设置属性方法*/

  38.  

    static ssize_t vp_lamp_show(struct device* dev, struct device_attribute* attr, char* buf);

  39.  

    static ssize_t vp_lamp_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);

  40.  

  41.  

    /*定义设备属性*/

  42.  

    static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, vp_lamp_show, vp_lamp_store);

  43.  

  44.  

  45.  

    /*******************************************

  46.  

    *define traditional file access

  47.  

    *******************************************/

  48.  

    /*打开设备方法*/

  49.  

    static int vp_open(struct inode* inode, struct file* filp) {

  50.  

    struct vp_android_dev* dev;

  51.  

  52.  

    /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/

  53.  

    dev = container_of(inode->i_cdev, struct vp_android_dev, dev);

  54.  

    filp->private_data = dev;

  55.  

  56.  

    return 0;

  57.  

    }

  58.  

  59.  

    /*设备文件释放时调用,空实现*/

  60.  

    static int vp_release(struct inode* inode, struct file* filp) {

  61.  

    return 0;

  62.  

    }

  63.  

  64.  

    /*读取设备的寄存器val的值*/

  65.  

    static ssize_t vp_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {

  66.  

    ssize_t err = 0;

  67.  

    struct vp_android_dev* dev = filp->private_data;

  68.  

  69.  

    /*同步访问*/

  70.  

    if(down_interruptible(&(dev->sem))) {

  71.  

    return -ERESTARTSYS;

  72.  

    }

  73.  

  74.  

    if(count < sizeof(dev->val)) {

  75.  

    goto out;

  76.  

    }

  77.  

  78.  

    /*将寄存器val的值拷贝到用户提供的缓冲区*/

  79.  

    if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {

  80.  

    err = -EFAULT;

  81.  

    goto out;

  82.  

    }

  83.  

  84.  

    err = sizeof(dev->val);

  85.  

  86.  

    out:

  87.  

    up(&(dev->sem));

  88.  

    return err;

  89.  

    }

  90.  

  91.  

    /*写设备的寄存器值val*/

  92.  

    static ssize_t vp_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {

  93.  

    struct vp_android_dev* dev = filp->private_data;

  94.  

    ssize_t err = 0;

  95.  

  96.  

    /*同步访问*/

  97.  

    if(down_interruptible(&(dev->sem))) {

  98.  

    return -ERESTARTSYS;

  99.  

    }

  100.  

  101.  

    if(count != sizeof(dev->val)) {

  102.  

    goto out;

  103.  

    }

  104.  

  105.  

    /*将用户提供的缓冲区的值写到设备寄存器去*/

  106.  

    if(copy_from_user(&(dev->val), buf, count)) {

  107.  

    err = -EFAULT;

  108.  

    goto out;

  109.  

    }

  110.  

  111.  

    err = sizeof(dev->val);

  112.  

  113.  

    out:

  114.  

    up(&(dev->sem));

  115.  

    return err;

  116.  

    }

  117.  

  118.  

  119.  

    /*******************************************

  120.  

    *define devfs access

  121.  

    *******************************************/

  122.  

    /*读取寄存器lamp的值到缓冲区buf中,内部使用*/

  123.  

    static ssize_t __vp_get_lamp(struct vp_android_dev* dev, char* buf) {

  124.  

    int lamp = 0;

  125.  

  126.  

    /*同步访问*/

  127.  

    if(down_interruptible(&(dev->sem))) {

  128.  

    return -ERESTARTSYS;

  129.  

    }

  130.  

  131.  

    lamp = dev->lamp;

  132.  

    up(&(dev->sem));

  133.  

  134.  

    return snprintf(buf, PAGE_SIZE, "%d\n", lamp);

  135.  

    }

  136.  

  137.  

    /*把缓冲区buf的值写到设备寄存器lamp中去,内部使用*/

  138.  

    static ssize_t __vp_set_lamp(struct vp_android_dev* dev, const char* buf, size_t count) {

  139.  

    int lamp = 0;

  140.  

  141.  

    /*将字符串转换成数字*/

  142.  

    lamp = simple_strtol(buf, NULL, 10);

  143.  

  144.  

    /*同步访问*/

  145.  

    if(down_interruptible(&(dev->sem))) {

  146.  

    return -ERESTARTSYS;

  147.  

    }

  148.  

  149.  

    dev->lamp = lamp;

  150.  

    up(&(dev->sem));

  151.  

  152.  

    return count;

  153.  

    }

  154.  

  155.  

    /*读取设备属性lamp*/

  156.  

    static ssize_t vp_lamp_show(struct device* dev, struct device_attribute* attr, char* buf) {

  157.  

    struct vp_android_dev* hdev = (struct vp_android_dev*)dev_get_drvdata(dev);

  158.  

  159.  

    return __vp_get_lamp(hdev, buf);

  160.  

    }

  161.  

  162.  

    /*写设备属性lamp*/

  163.  

    static ssize_t vp_lamp_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {

  164.  

    struct vp_android_dev* hdev = (struct vp_android_dev*)dev_get_drvdata(dev);

  165.  

  166.  

    return __vp_set_lamp(hdev, buf, count);

  167.  

    }

  168.  

  169.  

  170.  

    /*******************************************

  171.  

    *define proc access

  172.  

    *******************************************/

  173.  

    /*读取设备寄存器lamp的值,保存在page缓冲区中*/

  174.  

    static ssize_t vp_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) {

  175.  

    if(off > 0) {

  176.  

    *eof = 1;

  177.  

    return 0;

  178.  

    }

  179.  

  180.  

    return __vp_get_lamp(vp_dev, page);

  181.  

    }

  182.  

  183.  

    /*把缓冲区的值buff保存到设备寄存器lamp中去*/

  184.  

    static ssize_t vp_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) {

  185.  

    int err = 0;

  186.  

    char* page = NULL;

  187.  

  188.  

    if(len > PAGE_SIZE) {

  189.  

    printk(KERN_ALERT"The buff is too large: %lu.\n", len);

  190.  

    return -EFAULT;

  191.  

    }

  192.  

  193.  

    page = (char*)__get_free_page(GFP_KERNEL);

  194.  

    if(!page) {

  195.  

    printk(KERN_ALERT"Failed to alloc page.\n");

  196.  

    return -ENOMEM;

  197.  

    }

  198.  

  199.  

    /*先把用户提供的缓冲区值拷贝到内核缓冲区中去*/

  200.  

    if(copy_from_user(page, buff, len)) {

  201.  

    printk(KERN_ALERT"Failed to copy buff from user.\n");

  202.  

    err = -EFAULT;

  203.  

    goto out;

  204.  

    }

  205.  

  206.  

    err = __vp_set_lamp(vp_dev, page, len);

  207.  

  208.  

    out:

  209.  

    free_page((unsigned long)page);

  210.  

    return err;

  211.  

    }

  212.  

  213.  

    /*创建/proc/vp文件*/

  214.  

    static void vp_create_proc(void) {

  215.  

    struct proc_dir_entry *entry;

  216.  

    entry = create_proc_entry(VP_DEVICE_PROC_NAME, 0, NULL);

  217.  

    if(entry)

  218.  

    {

  219.  

    entry->read_proc = vp_proc_read;

  220.  

    entry->write_proc = vp_proc_write;

  221.  

    }

  222.  

    }

  223.  

  224.  

    /*删除/proc/vp文件*/

  225.  

    static void vp_remove_proc(void) {

  226.  

    remove_proc_entry(VP_DEVICE_PROC_NAME, NULL);

  227.  

    }

  228.  

  229.  

  230.  

    /*******************************************

  231.  

    *define load and remove function

  232.  

    *******************************************/

  233.  

    /*初始化设备*/

  234.  

    static int __vp_setup_dev(struct vp_android_dev* dev) {

  235.  

    int err;

  236.  

    dev_t devno = MKDEV(vp_major, vp_minor);

  237.  

  238.  

    memset(dev, 0, sizeof(struct vp_android_dev));

  239.  

  240.  

    cdev_init(&(dev->dev), &vp_fops);

  241.  

    dev->dev.owner = THIS_MODULE;

  242.  

    dev->dev.ops = &vp_fops;

  243.  

  244.  

    /*注册字符设备*/

  245.  

    err = cdev_add(&(dev->dev),devno, 1);

  246.  

    if(err) {

  247.  

    return err;

  248.  

    }

  249.  

  250.  

    /*初始化信号量和寄存器lamp, val的值*/

  251.  

    sema_init(&(dev->sem), 1);

  252.  

    dev->lamp = 7777;

  253.  

    dev->val.toggle = 1;

  254.  

    dev->val.virtual_latitude = 45.104108;

  255.  

    dev->val.virtual_longitude = 130.816878;

  256.  

  257.  

    return 0;

  258.  

    }

  259.  

  260.  

    /*模块加载方法*/

  261.  

    static int __init vp_init(void){

  262.  

    int err = -1;

  263.  

    dev_t dev = 0;

  264.  

    struct device* temp = NULL;

  265.  

  266.  

    printk(KERN_ALERT"Initializing vp device.\n");

  267.  

  268.  

    /*动态分配主设备和从设备号*/

  269.  

    err = alloc_chrdev_region(&dev, 0, 1, VP_DEVICE_NODE_NAME);

  270.  

    if(err < 0) {

  271.  

    printk(KERN_ALERT"Failed to alloc char dev region.\n");

  272.  

    goto fail;

  273.  

    }

  274.  

  275.  

    vp_major = MAJOR(dev);

  276.  

    vp_minor = MINOR(dev);

  277.  

  278.  

    /*分配helo设备结构体变量*/

  279.  

    vp_dev = kmalloc(sizeof(struct vp_android_dev), GFP_KERNEL);

  280.  

    if(!vp_dev) {

  281.  

    err = -ENOMEM;

  282.  

    printk(KERN_ALERT"Failed to alloc vp_dev.\n");

  283.  

    goto unregister;

  284.  

    }

  285.  

  286.  

    /*初始化设备*/

  287.  

    err = __vp_setup_dev(vp_dev);

  288.  

    if(err) {

  289.  

    printk(KERN_ALERT"Failed to setup dev: %d.\n", err);

  290.  

    goto cleanup;

  291.  

    }

  292.  

  293.  

    /*在/sys/class/目录下创建设备类别目录vp*/

  294.  

    vp_class = class_create(THIS_MODULE, VP_DEVICE_CLASS_NAME);

  295.  

    if(IS_ERR(vp_class)) {

  296.  

    err = PTR_ERR(vp_class);

  297.  

    printk(KERN_ALERT"Failed to create vp class.\n");

  298.  

    goto destroy_cdev;

  299.  

    }

  300.  

  301.  

    /*在/dev/目录和/sys/class/vp目录下分别创建设备文件vp*/

  302.  

    temp = device_create(vp_class, NULL, dev, "%s", VP_DEVICE_FILE_NAME);

  303.  

    if(IS_ERR(temp)) {

  304.  

    err = PTR_ERR(temp);

  305.  

    printk(KERN_ALERT"Failed to create vp device.");

  306.  

    goto destroy_class;

  307.  

    }

  308.  

  309.  

    /*在/sys/class/vp/vp目录下创建属性文件val*/

  310.  

    err = device_create_file(temp, &dev_attr_val);

  311.  

    if(err < 0) {

  312.  

    printk(KERN_ALERT"Failed to create attribute val.");

  313.  

    goto destroy_device;

  314.  

    }

  315.  

  316.  

    dev_set_drvdata(temp, vp_dev);

  317.  

  318.  

    /*创建/proc/vp文件*/

  319.  

    vp_create_proc();

  320.  

  321.  

    printk(KERN_ALERT"Succedded to initialize vp device.\n");

  322.  

    return 0;

  323.  

  324.  

    destroy_device:

  325.  

    device_destroy(vp_class, dev);

  326.  

  327.  

    destroy_class:

  328.  

    class_destroy(vp_class);

  329.  

  330.  

    destroy_cdev:

  331.  

    cdev_del(&(vp_dev->dev));

  332.  

  333.  

    cleanup:

  334.  

    kfree(vp_dev);

  335.  

  336.  

    unregister:

  337.  

    unregister_chrdev_region(MKDEV(vp_major, vp_minor), 1);

  338.  

  339.  

    fail:

  340.  

    return err;

  341.  

    }

  342.  

  343.  

    /*模块卸载方法*/

  344.  

    static void __exit vp_exit(void) {

  345.  

    dev_t devno = MKDEV(vp_major, vp_minor);

  346.  

  347.  

    printk(KERN_ALERT"Destroy vp device.\n");

  348.  

  349.  

    /*删除/proc/vp文件*/

  350.  

    vp_remove_proc();

  351.  

  352.  

    /*销毁设备类别和设备*/

  353.  

    if(vp_class) {

  354.  

    device_destroy(vp_class, MKDEV(vp_major, vp_minor));

  355.  

    class_destroy(vp_class);

  356.  

    }

  357.  

  358.  

    /*删除字符设备和释放设备内存*/

  359.  

    if(vp_dev) {

  360.  

    cdev_del(&(vp_dev->dev));

  361.  

    kfree(vp_dev);

  362.  

    }

  363.  

  364.  

    /*释放设备号*/

  365.  

    unregister_chrdev_region(devno, 1);

  366.  

    }

  367.  

  368.  

    MODULE_LICENSE("GPL");

  369.  

    MODULE_DESCRIPTION("Virtualposition Driver");

  370.  

  371.  

    module_init(vp_init);

  372.  

    module_exit(vp_exit);

添加 Kconfig 文件

  1. config VP

  2.  

    tristate "Virtual Position Driver"

  3.  

    default n

  4.  

    help

  5.  

    This is the virtual position driver.

添加 Makefile 文件

obj-$(CONFIG_VP) += vp.o

修改 drivers/Kconfig 文件 在menu "Device Drivers"和endmenu之间添加一行:

source "drivers/vp/Kconfig"  

修改drivers/Makefile文件,添加一行:

obj-$(CONFIG_HELLO) += vp/  

修改 arch/arm/configs目录下的cyanogen_cancro_defconfig 文件,在文件末尾加入

  1. # CONFIG_VP

  2.  

    CONFIG_VP=y

=============分割线2==============
第二步,修改HAL
进入 ./hardware/libhardware/include/hardware 目录,新建 vp.h 文件

  1. #ifndef ANDROID_VP_INTERFACE_H

  2.  

    #define ANDROID_VP_INTERFACE_H

  3.  

    #include <hardware/hardware.h>

  4.  

    __BEGIN_DECLS

  5.  

  6.  

    /*定义模块ID*/

  7.  

    #define VP_HARDWARE_MODULE_ID "vp"

  8.  

  9.  

  10.  

    //typedef enum{false, true} bool;

  11.  

  12.  

  13.  

    /*define virtual position structrue*/

  14.  

    typedef struct {

  15.  

    int toggle;

  16.  

    double virtual_latitude;

  17.  

    double virtual_longitude;

  18.  

    } VirtualPosition;

  19.  

  20.  

  21.  

    /*硬件模块结构体*/

  22.  

    struct vp_module_t {

  23.  

    struct hw_module_t common;

  24.  

    };

  25.  

  26.  

    /*硬件接口结构体*/

  27.  

    struct vp_device_t {

  28.  

    struct hw_device_t common;

  29.  

    int fd;

  30.  

    int (*set_val)(struct vp_device_t* dev, VirtualPosition val);

  31.  

    int (*get_val)(struct vp_device_t* dev, VirtualPosition* val);

  32.  

    };

  33.  

  34.  

    __END_DECLS

  35.  

  36.  

    #endif

进入到 hardware/libhardware/modules 目录,新建vp目录,并添加vp.c文件

  1. #define LOG_TAG "VpStub"

  2.  

  3.  

    #include <hardware/hardware.h>

  4.  

    #include <hardware/vp.h>

  5.  

    #include <fcntl.h>

  6.  

    #include <errno.h>

  7.  

    #include <cutils/log.h>

  8.  

    #include <cutils/atomic.h>

  9.  

  10.  

    #define DEVICE_NAME "/dev/vp"

  11.  

    #define MODULE_NAME "Vp"

  12.  

    #define MODULE_AUTHOR "[email protected]"

  13.  

  14.  

    /*设备打开和关闭接口*/

  15.  

    static int vp_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);

  16.  

    static int vp_device_close(struct hw_device_t* device);

  17.  

  18.  

    /*设备访问接口*/

  19.  

    static int vp_set_val(struct vp_device_t* dev, VirtualPosition val);

  20.  

    static int vp_get_val(struct vp_device_t* dev, VirtualPosition* val);

  21.  

  22.  

    /*模块方法表*/

  23.  

    static struct hw_module_methods_t vp_module_methods = {

  24.  

    open: vp_device_open

  25.  

    };

  26.  

  27.  

    /*模块实例变量*/

  28.  

    struct vp_module_t HAL_MODULE_INFO_SYM = {

  29.  

    common: {

  30.  

    tag: HARDWARE_MODULE_TAG,

  31.  

    version_major: 1,

  32.  

    version_minor: 0,

  33.  

    id: VP_HARDWARE_MODULE_ID,

  34.  

    name: MODULE_NAME,

  35.  

    author: MODULE_AUTHOR,

  36.  

    methods: &vp_module_methods,

  37.  

    }

  38.  

    };

  39.  

  40.  

  41.  

  42.  

  43.  

    static int vp_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {

  44.  

    struct vp_device_t* dev;dev = (struct vp_device_t*)malloc(sizeof(struct vp_device_t));

  45.  

  46.  

    if(!dev) {

  47.  

    ALOGE("Vp Stub: failed to alloc space");

  48.  

    return -EFAULT;

  49.  

    }

  50.  

  51.  

    memset(dev, 0, sizeof(struct vp_device_t));

  52.  

    dev->common.tag = HARDWARE_DEVICE_TAG;

  53.  

    dev->common.version = 0;

  54.  

    dev->common.module = (hw_module_t*)module;

  55.  

    dev->common.close = vp_device_close;

  56.  

    dev->set_val = vp_set_val;dev->get_val = vp_get_val;

  57.  

  58.  

    if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {

  59.  

    ALOGE("Vp Stub: failed to open /dev/vp -- %s.", strerror(errno));free(dev);

  60.  

    return -EFAULT;

  61.  

    }

  62.  

  63.  

    *device = &(dev->common);

  64.  

    ALOGI("Vp Stub: open /dev/vp successfully.");

  65.  

  66.  

    return 0;

  67.  

    }

  68.  

  69.  

  70.  

    static int vp_device_close(struct hw_device_t* device) {

  71.  

    struct vp_device_t* vp_device = (struct vp_device_t*)device;

  72.  

  73.  

    if(vp_device) {

  74.  

    close(vp_device->fd);

  75.  

    free(vp_device);

  76.  

    }

  77.  

  78.  

    return 0;

  79.  

    }

  80.  

  81.  

    static int vp_set_val(struct vp_device_t* dev, VirtualPosition val) {

  82.  

    ALOGI("Vp Stub: set value %d to device.", val);

  83.  

  84.  

    write(dev->fd, &val, sizeof(val));

  85.  

  86.  

    return 0;

  87.  

    }

  88.  

  89.  

    static int vp_get_val(struct vp_device_t* dev, VirtualPosition* val) {

  90.  

    if(!val) {

  91.  

    ALOGE("Vp Stub: error val pointer");

  92.  

    return -EFAULT;

  93.  

    }

  94.  

  95.  

    read(dev->fd, val, sizeof(*val));

  96.  

  97.  

    ALOGI("Vp Stub: get value %d from device", *val);

  98.  

  99.  

    return 0;

  100.  

    }

继续在vp目录下新建Android.mk文件:

  1. LOCAL_PATH := $(call my-dir)

  2.  

    include $(CLEAR_VARS)

  3.  

    LOCAL_MODULE_TAGS := optional

  4.  

    LOCAL_PRELINK_MODULE := false

  5.  

    LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw

  6.  

    LOCAL_SHARED_LIBRARIES := liblog

  7.  

    LOCAL_SRC_FILES := vp.c

  8.  

    LOCAL_MODULE := vp.default

  9.  

    include $(BUILD_SHARED_LIBRARY)

=============分割线3==============
第三步,修改JNI
进入 frameworks/base/services/core/jni 目录,新建com_android_server_VirtualPositionService.cpp文件

  1. #define LOG_TAG "VirtualPositionService"

  2.  

    #include "jni.h"

  3.  

    #include "JNIHelp.h"

  4.  

    #include "android_runtime/AndroidRuntime.h"

  5.  

    #include <utils/misc.h>

  6.  

    #include <utils/Log.h>

  7.  

    #include <hardware/hardware.h>

  8.  

    #include <hardware/vp.h>

  9.  

    #include <stdio.h>

  10.  

  11.  

  12.  

    namespace android

  13.  

    {

  14.  

  15.  

    VirtualPosition virtual_position = {1, 0.0, 0.0};

  16.  

    /*在硬件抽象层中定义的硬件访问结构体,参考<hardware/vp.h>*/

  17.  

    struct vp_device_t* vp_device = NULL;

  18.  

    /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/

  19.  

    static void vp_setVal() {

  20.  

    ALOGI("VirtualPosition JNI: set value to device.");

  21.  

    if(!vp_device) {

  22.  

    ALOGI("VirtualPosition JNI: device is not open.");

  23.  

    return;

  24.  

    }

  25.  

  26.  

    vp_device->set_val(vp_device, virtual_position);

  27.  

    }

  28.  

    /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/

  29.  

    static void vp_getVal() {

  30.  

    if(!vp_device) {

  31.  

    ALOGI("VirtualPosition JNI: device is not open.");

  32.  

    }

  33.  

    vp_device->get_val(vp_device, &virtual_position);

  34.  

  35.  

    ALOGI("VirtualPosition JNI: get value from device.");

  36.  

    }

  37.  

    /*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/

  38.  

    static inline int vp_device_open(const hw_module_t* module, struct vp_device_t** device) {

  39.  

    return module->methods->open(module, VP_HARDWARE_MODULE_ID, (struct hw_device_t**)device);

  40.  

    }

  41.  

    /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/

  42.  

    static jboolean vp_init(JNIEnv* env, jclass clazz) {

  43.  

    vp_module_t* module;

  44.  

  45.  

    ALOGI("VirtualPosition JNI: initializing......");

  46.  

    if(hw_get_module(VP_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {

  47.  

    ALOGI("VirtualPosition JNI: vp Stub found.");

  48.  

    if(vp_device_open(&(module->common), &vp_device) == 0) {

  49.  

    ALOGI("VirtualPosition JNI: vp device is open.");

  50.  

    return 0;

  51.  

    }

  52.  

    ALOGE("VirtualPosition JNI: failed to open vp device.");

  53.  

    return -1;

  54.  

    }

  55.  

    ALOGE("VirtualPosition JNI: failed to get vp stub module.");

  56.  

    return -1;

  57.  

    }

  58.  

  59.  

    static void android_server_VirtualPositionService_set_virtual_toggle(JNIEnv* env, jclass clazz, jint tog)

  60.  

    {

  61.  

    virtual_position.toggle = tog;

  62.  

    vp_setVal();

  63.  

    }

  64.  

  65.  

  66.  

    static jint android_server_VirtualPositionService_get_virtual_toggle(JNIEnv* env, jclass clazz)

  67.  

    {

  68.  

    vp_getVal();

  69.  

    return virtual_position.toggle;

  70.  

    }

  71.  

  72.  

  73.  

    static void android_server_VirtualPositionService_set_virtual_latitude(JNIEnv* env, jclass clazz, jdouble vlat)

  74.  

    {

  75.  

    virtual_position.virtual_latitude = vlat;

  76.  

    vp_setVal();

  77.  

    }

  78.  

  79.  

  80.  

    static jdouble android_server_VirtualPositionService_get_virtual_latitude(JNIEnv* env, jclass clazz)

  81.  

    {

  82.  

    vp_getVal();

  83.  

    return virtual_position.virtual_latitude;

  84.  

    }

  85.  

  86.  

  87.  

    static void android_server_VirtualPositionService_set_virtual_longitude(JNIEnv* env, jclass clazz, jdouble vlon)

  88.  

    {

  89.  

    virtual_position.virtual_longitude = vlon;

  90.  

    vp_setVal();

  91.  

    }

  92.  

  93.  

  94.  

    static jdouble android_server_VirtualPositionService_get_virtual_longitude(JNIEnv* env, jclass clazz)

  95.  

    {

  96.  

    vp_getVal();

  97.  

    return virtual_position.virtual_longitude;

  98.  

    }

  99.  

  100.  

  101.  

  102.  

  103.  

  104.  

  105.  

    /*JNI方法表*/

  106.  

    static const JNINativeMethod method_table[] = {

  107.  

    {"init_native",

  108.  

    "()Z",

  109.  

    (void*)vp_init},

  110.  

    {"native_set_virtual_toggle",

  111.  

    "(I)V",

  112.  

    (void*)android_server_VirtualPositionService_set_virtual_toggle},

  113.  

    {"native_get_virtual_toggle",

  114.  

    "()I",

  115.  

    (void*)android_server_VirtualPositionService_get_virtual_toggle},

  116.  

    {"native_set_virtual_latitude",

  117.  

    "(D)V",

  118.  

    (void*)android_server_VirtualPositionService_set_virtual_latitude},

  119.  

    {"native_get_virtual_latitude",

  120.  

    "()D",

  121.  

    (void*)android_server_VirtualPositionService_get_virtual_latitude},

  122.  

    {"native_set_virtual_longitude",

  123.  

    "(D)V",

  124.  

    (void*)android_server_VirtualPositionService_set_virtual_longitude},

  125.  

    {"native_get_virtual_longitude",

  126.  

    "()D",

  127.  

    (void*)android_server_VirtualPositionService_get_virtual_longitude},

  128.  

  129.  

  130.  

    };

  131.  

    /*注册JNI方法*/

  132.  

    int register_android_server_VirtualPositionService(JNIEnv *env) {

  133.  

    return jniRegisterNativeMethods(env, "com/android/server/VirtualPositionService", method_table, NELEM(method_table));

  134.  

    }

  135.  

    };

修改同目录下的onload.cpp文件,首先在namespace android增加com_android_server_VirtualPositionService函数声明:

  1. namespace android {

  2.  

  3.  

    ..............................................................................................

  4.  

  5.  

    int register_android_server_VirtualPositionService(JNIEnv *env);

  6.  

  7.  

    };

  8.  

    在JNI_onLoad增加register_android_server_VirtualPositionService函数调用:

  9.  

    extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)

  10.  

    {

  11.  

    .................................................................................................

  12.  

    register_android_server_VirtualPositionService(env);

  13.  

    .................................................................................................

  14.  

    }

修改同目录下的Android.mk文件,在LOCAL_SRC_FILES变量中增加一行:

  1. LOCAL_SRC_FILES:= \

  2.  

    com_android_server_AlarmManagerService.cpp \

  3.  

    com_android_server_BatteryService.cpp \

  4.  

    com_android_server_InputManager.cpp \

  5.  

    com_android_server_LightsService.cpp \

  6.  

    com_android_server_PowerManagerService.cpp \

  7.  

    com_android_server_SystemServer.cpp \

  8.  

    com_android_server_UsbService.cpp \

  9.  

    com_android_server_VibratorService.cpp \

  10.  

    com_android_server_location_GpsLocationProvider.cpp \

  11.  

    com_android_server_VirtualPositionService.cpp \

  12.  

    onload.cpp

=============分割线4==============
第四步,修改Framework
进入到frameworks/base/core/java/android/os目录,新增VirtualPositionService.aidl接口定义文件

  1. package android.os;

  2.  

  3.  

  4.  

    interface IVirtualPositionService {

  5.  

    void setVirtualToggle(int tog);

  6.  

    int getVirtualToggle();

  7.  

    void setVirtualLatitude(double vlat);

  8.  

    double getVirtualLatitude();

  9.  

    void setVirtualLongitude(double vlon);

  10.  

    double getVirtualLongitude();

  11.  

    }

然后进入 frameworks/base目录,打开Android.mk文件,修改LOCAL_SRC_FILES变量的值,增加IVirtualPosition.aidl源文件:

  1. LOCAL_SRC_FILES += /

  2.  

    ....................................................................

  3.  

    core/java/android/os/IVibratorService.aidl /

  4.  

    core/java/android/os/IVirtualPosition.aidl /

  5.  

    core/java/android/service/urlrenderer/IUrlRendererService.aidl /

  6.  

    .....................................................................

进入到frameworks/base/services/java/com/android/server目录,新增VirtualPositionService.java文件

  1. package com.android.server;

  2.  

    import android.content.Context;

  3.  

    import android.os.IVirtualPositionService;

  4.  

    import android.util.Slog;

  5.  

  6.  

  7.  

    public class VirtualPositionService extends IVirtualPositionService.Stub {

  8.  

    private static final String TAG = "VirtualPositionService";

  9.  

    VirtualPositionService() {

  10.  

    init_native();

  11.  

    }

  12.  

    public void setVirtualToggle(int tog) {

  13.  

    native_set_virtual_toggle(tog);

  14.  

    }

  15.  

  16.  

  17.  

    public int getVirtualToggle(){

  18.  

    return native_get_virtual_toggle();

  19.  

    }

  20.  

  21.  

  22.  

    public void setVirtualLatitude(double vlat) {

  23.  

    native_set_virtual_latitude(vlat);

  24.  

    }

  25.  

  26.  

  27.  

    public double getVirtualLatitude(){

  28.  

    return native_get_virtual_latitude();

  29.  

    }

  30.  

  31.  

  32.  

    public void setVirtualLongitude(double vlon) {

  33.  

    native_set_virtual_longitude(vlon);

  34.  

    }

  35.  

  36.  

  37.  

    public double getVirtualLongitude() {

  38.  

    return native_get_virtual_longitude();

  39.  

    }

  40.  

  41.  

    private static native boolean init_native();

  42.  

    private static native void native_set_virtual_toggle(int tog);

  43.  

    private static native int native_get_virtual_toggle();

  44.  

    private static native void native_set_virtual_latitude(double vlat);

  45.  

    private static native double native_get_virtual_latitude();

  46.  

    private static native void native_set_virtual_longitude(double vlon);

  47.  

    private static native double native_get_virtual_longitude();

  48.  

  49.  

  50.  

    };

修改同目录的SystemServer.java文件,在ServerThread::run函数中增加加载VirtualPositionService的代码:

  1. @Override

  2.  

    public void run() {

  3.  

    ....................................................................................

  4.  

    try {

  5.  

    Slog.i(TAG, "DiskStats Service");

  6.  

    ServiceManager.addService("diskstats", new DiskStatsService(context));

  7.  

    } catch (Throwable e) {

  8.  

    Slog.e(TAG, "Failure starting DiskStats Service", e);

  9.  

    }

  10.  

    try {

  11.  

    Slog.i(TAG, "VirtualPosition Service");

  12.  

    ServiceManager.addService("virtualposition", new VirtualPositionService());

  13.  

    } catch (Throwable e) {

  14.  

    Slog.e(TAG, "Failure starting VirtualPosition Service", e);

  15.  

    }

  16.  

    ......................................................................................

  17.  

    }

然后需要修改sepolicy文件,具体的文件在github上,请下载使用。

=============分割线5==============
第五步,修改application

  1. package com.example.phdemo.myapplication;

  2.  

  3.  

  4.  

    import android.os.RemoteException;

  5.  

    import android.app.Activity;

  6.  

    import android.os.ServiceManager;

  7.  

    import android.os.Bundle;

  8.  

    import android.widget.CompoundButton;

  9.  

    import android.os.IVirtualPositionService;

  10.  

    import android.os.RemoteException;

  11.  

    import android.util.Log;

  12.  

    import android.view.View;

  13.  

    import android.view.View.OnClickListener;

  14.  

    import android.widget.Button;

  15.  

    import android.widget.EditText;

  16.  

    import android.widget.ToggleButton;

  17.  

    import android.widget.CompoundButton.OnCheckedChangeListener;

  18.  

  19.  

  20.  

    public class MainActivity extends Activity implements View.OnClickListener {

  21.  

  22.  

  23.  

    private final static String LOG_TAG = "com.example.phdemo.virtualposition";

  24.  

  25.  

  26.  

    private IVirtualPositionService virtualpositionService = null;

  27.  

    private ToggleButton toggleButton = null;

  28.  

    private EditText altitudeValueText = null;

  29.  

    private EditText longitudeValueText = null;

  30.  

    private Button getButton = null;

  31.  

    private Button setButton = null;

  32.  

    private Button clearButton = null;

  33.  

  34.  

  35.  

    /** Called when the activity is first created. */

  36.  

    @Override

  37.  

    public void onCreate(Bundle savedInstanceState) {

  38.  

    super.onCreate(savedInstanceState);

  39.  

    setContentView(R.layout.activity_main);

  40.  

  41.  

  42.  

    virtualpositionService = IVirtualPositionService.Stub.asInterface(

  43.  

    ServiceManager.getService("virtualposition"));

  44.  

  45.  

  46.  

    toggleButton=(ToggleButton)findViewById(R.id.toggleButton);

  47.  

    altitudeValueText = (EditText)findViewById(R.id.altitude_value);

  48.  

    longitudeValueText = (EditText)findViewById(R.id.longitude_value);

  49.  

    getButton = (Button)findViewById(R.id.button_get);

  50.  

    setButton = (Button)findViewById(R.id.button_set);

  51.  

    clearButton = (Button)findViewById(R.id.button_clear);

  52.  

  53.  

    getButton.setOnClickListener(this);

  54.  

    setButton.setOnClickListener(this);

  55.  

    clearButton.setOnClickListener(this);

  56.  

  57.  

    try{

  58.  

    int val_tog = virtualpositionService.getVirtualToggle();

  59.  

    if(val_tog == 1){

  60.  

    toggleButton.setChecked(true);

  61.  

    }else{

  62.  

    toggleButton.setChecked(false);

  63.  

    }

  64.  

    } catch (Exception e) {}

  65.  

  66.  

  67.  

    toggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener(){

  68.  

    public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {

  69.  

    toggleButton.setChecked(isChecked);

  70.  

    try{

  71.  

    virtualpositionService.setVirtualToggle(isChecked?1:0);

  72.  

    }catch(Exception e){}

  73.  

    }

  74.  

    });

  75.  

    Log.i(LOG_TAG, "VirtualPosition Activity Created");

  76.  

    }

  77.  

  78.  

  79.  

    @Override

  80.  

    public void onClick(View v) {

  81.  

    if(v.equals(getButton)) {

  82.  

    try {

  83.  

    double val_altitude = virtualpositionService.getVirtualLatitude();

  84.  

    String text_altitude = String.valueOf(val_altitude);

  85.  

    altitudeValueText.setText(text_altitude);

  86.  

    double val_longitude = virtualpositionService.getVirtualLongitude();

  87.  

    String text_longitude = String.valueOf(val_longitude);

  88.  

    longitudeValueText.setText(text_longitude);

  89.  

    int val_tog = virtualpositionService.getVirtualToggle();

  90.  

    if(val_tog == 1){

  91.  

    toggleButton.setChecked(true);

  92.  

    }else{

  93.  

    toggleButton.setChecked(false);

  94.  

    }

  95.  

    } catch (Exception e) {

  96.  

    Log.e(LOG_TAG, "Remote Exception while reading value from GpsLocationProvider.");

  97.  

    }

  98.  

    }

  99.  

    else if(v.equals(setButton)) {

  100.  

    try {

  101.  

    String text_altitude = altitudeValueText.getText().toString();

  102.  

    String text_longitude = longitudeValueText.getText().toString();

  103.  

    double val_altitude = Double.parseDouble(text_altitude);

  104.  

    double val_longitude = Double.parseDouble(text_longitude);

  105.  

    virtualpositionService.setVirtualLatitude(val_altitude);

  106.  

    virtualpositionService.setVirtualLongitude(val_longitude);

  107.  

    } catch (Exception e) {

  108.  

    Log.e(LOG_TAG, "Remote Exception while writing value to GpsLocationProvider.");

  109.  

    }

  110.  

    }

  111.  

    else if(v.equals(clearButton)) {

  112.  

    String text = "";

  113.  

    altitudeValueText.setText(text);

  114.  

    longitudeValueText.setText(text);

  115.  

    }

  116.  

    }

  117.  

    }

=============分割线6==============
最后一步,在JNI层面修改location report 机制。
进入 frameworks/base/services/core/jni 目录,修改com_android_server_location_GpsLocationProvider.cpp文件:
在全局变量部分加入

  1. // add by aggresss

  2.  

    static int vp_fd = open("/dev/vp", O_RDWR);

  3.  

    static VirtualPosition vp_val;

修改location_callback函数:

  1. static void location_callback(GpsLocation* location)

  2.  

    {

  3.  

    JNIEnv* env = AndroidRuntime::getJNIEnv();

  4.  

    //add by aggresss

  5.  

    read(vp_fd, &vp_val, sizeof(VirtualPosition));

  6.  

    if(vp_val.toggle == 1){

  7.  

    env->CallVoidMethod(mCallbacksObj, method_reportLocation, location->flags,

  8.  

    (jdouble)vp_val.virtual_latitude, (jdouble)vp_val.virtual_longitude,

  9.  

    (jdouble)location->altitude,

  10.  

    (jfloat)location->speed, (jfloat)location->bearing,

  11.  

    (jfloat)location->accuracy, (jlong)location->timestamp);

  12.  

    checkAndClearExceptionFromCallback(env, __FUNCTION__);

  13.  

    }

  14.  

    else{

  15.  

    env->CallVoidMethod(mCallbacksObj, method_reportLocation, location->flags,

  16.  

    (jdouble)location->latitude, (jdouble)location->longitude,

  17.  

    (jdouble)location->altitude,

  18.  

    (jfloat)location->speed, (jfloat)location->bearing,

  19.  

    (jfloat)location->accuracy, (jlong)location->timestamp);

  20.  

    checkAndClearExceptionFromCallback(env, __FUNCTION__);

  21.  

    }

  22.  

    }

=============我是完成的分割线==============
完成后,重新编译固件,开机后启动VirtualPosition的APP,设置你想要的坐标,想在哪就在哪了。

原文地址:https://www.cnblogs.com/shenlekeji/p/9381611.html

时间: 2024-10-24 10:26:03

手机虚拟定位 可以在家里钉钉打卡的相关文章

今天教大家苹果系统钉钉打卡怎么作弊 钉钉签到水印照片怎么替换

随着移动互联网的不断深入,很多公司已经不在用传统的指纹打卡机了,采用了软件定位打卡.钉钉作为阿里巴巴的主推产品,已经有越来越多的公司在使用.但是公司死板的考勤制度让很多人觉得很头疼,不知道该如果破解? 今天就给大家推荐一个钉钉虚拟定位的方法,可以考勤打卡位置修改,也可以打卡定位破解. 而且钉钉也是频繁升级,找到一个靠谱的软件,难上加难.而且市面上鱼龙混杂,要仔细擦亮眼睛,关乎到自己的工作,面子,真正不是开玩笑,一旦出现问题,后悔是来不及的. 因此我们工作室最近推出了一款,软件,可以完美解决困扰大

一秒教会你破解企业微信虚拟定位位置修改 让你在哪都可以打卡上下班

一秒教会你破解企业微信虚拟定位位置修改  让你在哪都可以打卡上下班 一秒教会你破解企业微信虚拟定位位置修改  让你在哪都可以打卡上下班 一秒教会你破解企业微信虚拟定位位置修改  让你在哪都可以打卡上下班 企业微信除了具有类似微信的聊天功能,另外添加了公费电话和邮件功能.腾讯方面表示,企业微信将免费使用.在OA功能方面,结合了公告.考勤.请假.报销.此外,企业微信添加了如回执消息.休息一下等办公场景功能.企业微信原来是想成为企业内部通信工具,对标钉钉,然而现在很多企业还是在用微信做内部沟通.这一次

钉钉改定位打卡,在家也能打卡!钉钉虚拟定位打卡实测!(2019-6-14亲测)

一.GPS工程模块 即插即用,通过专用的APP即可实现对位置的修改.通过IOS的开发者协议,用模块打开手机的开发者模式然后进行GPS调试,从而实现钉钉改定位打卡!下图中的4个S就是专用App 二.电脑软件---移动边际 ①下载电脑软件 移动边际 从而实现钉钉改定位打卡!链接: https://pan.baidu.com/s/1-ummQGYNjjKVnEFVtA2MVA 提取码: vej5 ②关闭杀毒安全类软件,以免被误杀.解压压缩包后直接运行移动边际,手机通过数据线连接电脑. ③手机连接电脑后

教大家一个钉钉考勤打卡定位更改的好方法可以考勤打卡改位置模拟WiFi模拟水印照片

随着智能办公的普及,钉钉成为了很多公司办公软件.上班族上下班考勤打卡一般可通过"钉钉"定位到公司位置进行打卡,但由于一些特殊原因,定位不准确,或者不能及时定位打卡.虚拟上神教你解决"钉钉"考勤的问题,实现上班族随时随地定位考勤用收藏地址随时打卡考勤.能解决上下班考勤,让你再也不会迟到的! 最近钉钉又更新到了4.3.2版本,面对钉钉如此之快的更新速度,许多上班族是更加头痛了,因为每一次的更新就有可能修复之前的BUG,导致自己的虚拟定位软件用不了,那么面对钉钉的持续更新

关于钉钉如何修改位置打卡签到,在家打卡,钉钉虚拟位置运用教程

上班一族都很头疼这件事,上班打卡,迟到了扣工资,心痛.....关于钉钉如何进行虚拟定位然后正常打卡,有一下方案推荐给大家,大家可以也去试一试! ①系统定位软件 此软件是基于苹果系统代码,编写的,无需越狱直接使用软件某云自提,链接: https://pan.baidu.com/s/13ZSedj3Z78lNmNh5PzJe9w 提取码: f2gj 下载安装itunes 打开moveaway 文件夹中的injecttool 打开moveaway程序附:如何查看 自 己的系统是32位还是64位? 按w

vue中,使用手机钉钉扫描二维码登录

最新项目要做一个,使用手机钉钉扫描二维码登录pc系统的功能,手机扫码二维码后,会弹出一个确定登录的页面,点击确定之后,pc端就会登录进去 第一步:查看钉钉开发平台 钉钉开发平台(钉钉官网) 从官网中了解到: 使用钉钉js-api提供的获取免登授权码接口获取CODE,此jsapi无需鉴权 然后通过CODE,获取用户身份信息 第二步:pc页面 npm install v-qrcode --save 并在页面中注册引入 其中  qrcode是二维码内容,在data中定义, 调用后端接口,获取钉钉登录二

springmvc请求参数异常统一处理,结合钉钉报告信息定位bug位置

参考之前一篇博客:springmvc请求参数异常统一处理 1.ExceptionHandlerController package com.oy.controller; import java.text.MessageFormat; import java.util.ResourceBundle; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.TypeMismatchException

不越狱修改钉钉位置打卡(实现除了砸壳,都可以在不越狱的前提下进行逆向工程)

接触逆向工程也有一点时间了,从最初研究我水哥的小黄书(iOS应用逆向工程),到后来自己摩拳擦掌准备实练,突然发现自己没有一款越狱手机,以至于很多iOS工具都无法使用.这就很纠结.突然发现网易的大佬 庆哥把iOSOpenDev(13年已停更)修改升级除了 MonkeyDev ,这个东西有多好用.简单来说,他就已经注入dylib,集成Reveal.Cycript等等,不需要自己搞去配置. 一切的一切,只需要自己配置好最新的 theos 环境.   1 sudo git clone --recursi

(教程)企业微信如何虚拟定位打卡,在家打卡,不再迟到!

近两年很多企业和中小型公司都开始使用钉钉打卡签到.很多苦逼党因为坐公交晚了几分钟,被扣钱,晚了几分钟,全勤没了,所以这里我们可以缓解下代码狗的痛苦 -- 模拟定位(先打卡,再到公司).已经会连Xcode模拟定位的可以忽略前面的部分内容,直接跳到最后. 公司设定打卡范围,100米,500米,1公里都可以,但是基于有模拟定位这个技术,钉钉在打卡选项里加了一项WiFi打卡,定位打卡和WiFi打开可以叠加存在以保证有人打卡作弊 坐标系统 这里普及一下坐标系统:目前我们经常接触的无非就是原始坐标,火星坐标