自己写按键精灵——用SimulateTouch模拟点击及滑动

按键精灵,App分析,挂机助手,游戏刷分是iOS上点击量多,重复性大的工作。这些机械的工作完全由人工来完成不免显得有些大材小用,如果能解放我们,放手让机器去全自动化地完成这一系列操作,岂不美哉?
但是,模拟触摸的操作没有在SDK里公开,需要比较扎实的逆向工程基础才能厘清这个功能在底层的来龙去脉,对于逆向工程的初学者来说,任务难度太大。好在一名来自韩国的开发者iolate帮助我们完成了这个功能实现中难度最大的一部分代码,不但将它们封装成了简单的几个函数调用,还慷慨地开源了所有代码,令人敬佩。下面我们就简单地看看iolate的SimulateTouch如何使用。

一、在Cydia中搜索并下载SimulateTouch
下载完成后,将其中的/usr/lib/libsimulatetouch.dylib拷贝到$THEOS/lib/libsimulatetouch.dylib下

二、用Theos建立一个测试工程iOSRETouch
没什么好说的,这一步已经在书上和论坛上讲过无数遍了。

三、下载SimulateTouch.h
GitHub上下载SimulateTouch.h文件,将其放入工程目录下。

四、修改Makefile,链接libsimulatetouch.dylib

THEOS_DEVICE_IP = localhost
THEOS_DEVICE_PORT = 2222
ARCHS = armv7 armv7s arm64
TARGET = iphone:7.0:6.0 # 注:新版SimulateTouch已支持7.1.x,此处可改为TARGET = iphone:7.1:6.0

include theos/makefiles/common.mk

TOOL_NAME = iOSRETouch
iOSRETouch_FILES = main.mm
iOSRETouch_LDFLAGS = -lsimulatetouch # 这里必须链接libsimulatetouch.dylib

include $(THEOS_MAKE_PATH)/tool.mk

注意,因为SimulateTouch目前仅支持6.x ~ 7.0.x,所以我们的TARGET填写的是7.0,请大家注意。2014-7-17编辑:新版SimulateTouch已支持7.1.x。

五、编写代码
参考iolate给出的官方API文档,点击和滑动事件用到的API分别是

+ (int)simulateTouch:(int)pathIndex atPoint:(CGPoint)point withType:(STTouchType)type;
和
+ (int)simulateSwipeFromPoint:(CGPoint)fromPoint toPoint:(CGPoint)toPoint duration:(float)duration;

参数及返回值的含义在文档里已经说得非常清楚了。继续参考官方给出的一个例子,我们的main.mm可以写成这样:

#import "SimulateTouch.h"

int main(int argc, char **argv, char **envp)
{
        if (argc != 4 && argc != 6) printf("Usage:\nTouch (x, y): iOSRETouch touch x y\nSwipe from (a, b) to (c, d): iOSRETouch swipe a b c d\n");
        else if (argc == 4 && strcmp(argv[1], "touch") == 0) // iOSRETouch touch x y
        {
                int x = atoi(argv[2]);
                int y = atoi(argv[3]);

                CGPoint touchPoint = CGPointMake(x, y);
                int r = [SimulateTouch simulateTouch:0 atPoint:touchPoint withType:STTouchDown];
                if (r == 0) printf("iOSREError: Simutale touch down failed at (%d, %d).\n", x, y);
                r = [SimulateTouch simulateTouch:r atPoint:touchPoint withType:STTouchUp];
                if (r == 0) printf("iOSREError: Simutale touch up failed at (%d, %d).\n", x, y);
        }
        else if (argc == 6 && strcmp(argv[1], "swipe") == 0) // iOSRETouch swipe a b c d
        {
                int a = atoi(argv[2]);
                int b = atoi(argv[3]);
                int c = atoi(argv[4]);
                int d = atoi(argv[5]);

                CGPoint startSwipingPoint = CGPointMake(a, b);
                CGPoint endSwipingPoint = CGPointMake(c, d);
                int r = [SimulateTouch simulateSwipeFromPoint:startSwipingPoint toPoint:endSwipingPoint duration:0.2f];
                if (r == 0) printf("iOSREError: Failed to simutale swipe from (%d, %d) to (%d, %d).\n", a, b, c, d);

                CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.3f, NO); // 注意在模拟滑动时,这一行一定不能漏
        }

        return 0;
}

编译安装之后,就可以ssh到iOS上,然后通过命令

iOSRETouch touch x y

在(x, y)坐标处模拟点击;或者通过命令

iOSRETouch swipe a b c d

模拟从(a, b)滑动到(c, d)。

六、测试
打开“备忘录”,查看一个条目的详细内容界面,然后在iOS命令行里输入命令

iOSRETouch touch 6 6

看看这个左上角的“备忘录”返回按钮是不是被点击了?
接着输入命令

iOSRETouch swipe 60 100 60 200

看看备忘录的搜索栏是不是被我们滑出来了?好了,点击和滑动事件均工作正常,测试成功!

就是这么简单。赶紧开始把强大的SimulateTouch整合到自己的App里去吧!

6 个赞

实测iOS 7.1下不能用。

请留意部分,已经说明仅支持到7.0.x

1 个赞

不好意思。
看github今天更新了,修正了这个问题。

请问这个问题出在哪呢?谢谢

dyld: Library not loaded: /usr/lib//libsimulatetouch.dylib
  Referenced from: /usr/bin/ithincoST
  Reason: image not found

没在Cydia里装SimulateTouch吧?

还以为是make时链接出错了。。十分感谢!

能否把SimulateTouch整合到自己的工程中?

我这个例子不就是把它整合到自己的工程里了吗?

libsimulatetouch.dylib,这个库也可以自己编译的吧,是不是github上
SimulateTouch.mm 编译出来的,还是STLibrary.mm?
疑问如下:
从官方的makefile 来看SimulateTouch.mm 这个是编译成一个deb包,STLibrary.mm这个是编译成一个库,群主上面提到安装SimulateTouch这个插件复制出动态库是指这个STLibrary.mm直接编译出来的吗?SimulateTouch.mm 这个编译出来deb包是hook用?

不是,这个库应该没有提供记录的功能,我是直接在iOS上安装了SimulateTouch后从文件系统里拷贝除了dylib

感觉版本有两个吧,上半年我下载到时候有如下的函数:

static void LogTouchEvent(IOHIDEventRef event) {
    
    if (IOHIDEventGetType(event) != 11) return;
    
    NSString* logString = nil;
    
    logString = [NSString stringWithFormat:@"\nST\t%d %d (%d %d)",
                 IOHIDEventGetIntegerValue(event, kIOHIDEventFieldDigitizerEventMask), IOHIDEventGetIntegerValue(event, kIOHIDEventFieldDigitizerIndex), IOHIDEventGetIntegerValue(event, kIOHIDEventFieldDigitizerRange), IOHIDEventGetIntegerValue(event, kIOHIDEventFieldDigitizerTouch)];
    
        CFArrayRef children = IOHIDEventGetChildren(event);
    
    for (int i = 0; i < CFArrayGetCount(children); i++) {
                IOHIDEventRef e = (IOHIDEventRef)CFArrayGetValueAtIndex(children, i);
                logString = [logString stringByAppendingFormat:@"\nST\t\t%d %d (%d %d)", IOHIDEventGetIntegerValue(e, kIOHIDEventFieldDigitizerEventMask), IOHIDEventGetIntegerValue(e, kIOHIDEventFieldDigitizerIdentity), IOHIDEventGetIntegerValue(e, kIOHIDEventFieldDigitizerRange), IOHIDEventGetIntegerValue(e, kIOHIDEventFieldDigitizerTouch)];
        }
        
        if (prevString != nil && [prevString isEqualToString:logString]){
                count++;
        }else{
                if (count != 0) {
                        NSLog(@"ST Last repeated %d times", count);
                        count  = 0;
                }
                NSLog(@"%@", logString);
        }
    
        prevString = [logString retain];
    
}

这个好感觉可以记录日志的啊,
不过群主之前在干货分享的时候,下载下来好象又注释了如上的函数,并且最新代码里也注释如下函数,感觉里面也有记录相关点击的坐标啊

[table]
[tr][td][/td][td]/*[/td][/tr]
[tr][td][/td][td]MSHook(IOHIDEventRef, IOHIDEventCreateDigitizerEvent, CFAllocatorRef allocator, AbsoluteTime timeStamp, IOHIDDigitizerTransducerType type,[/td][/tr]
[tr][td][/td][td]       uint32_t index, uint32_t identity, uint32_t eventMask, uint32_t buttonMask,[/td][/tr]
[tr][td][/td][td]       IOHIDFloat x, IOHIDFloat y, IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat barrelPressure,[/td][/tr]
[tr][td][/td][td]       Boolean range, Boolean touch, IOOptionBits options) {[/td][/tr]
[tr][td][/td][td]    [/td][/tr]
[tr][td][/td][td]    //NSLog(@"##### Event %d", type);[/td][/tr]
[tr][td][/td][td]    //NSLog(@"##### Event %d %d %d %d %d (%f, %f, %f) %f %f %d %d %d", type, index, identity, eventMask, buttonMask, x, y, z, tipPressure, barrelPressure, range, touch, (unsigned int)options);[/td][/tr]
[tr][td][/td][td]    return _IOHIDEventCreateDigitizerEvent(allocator, timeStamp, type, index, identity, eventMask, buttonMask, x, y, z, tipPressure, barrelPressure, range, touch, options);[/td][/tr]
[tr][td][/td][td]}[/td][/tr]
[tr][td][/td][td]MSHook(IOHIDEventRef, IOHIDEventCreateDigitizerFingerEventWithQuality, CFAllocatorRef allocator, AbsoluteTime timeStamp,[/td][/tr]
[tr][td][/td][td]       uint32_t index, uint32_t identity, uint32_t eventMask,[/td][/tr]
[tr][td][/td][td]       IOHIDFloat x, IOHIDFloat y, IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat twist,[/td][/tr]
[tr][td][/td][td]       IOHIDFloat minorRadius, IOHIDFloat majorRadius, IOHIDFloat quality, IOHIDFloat density, IOHIDFloat irregularity,[/td][/tr]
[tr][td][/td][td]       Boolean range, Boolean touch, IOOptionBits options) {[/td][/tr]
[tr][td][/td][td]    [/td][/tr]
[tr][td][/td][td]    //NSLog(@"##### Quality %d %d %d %f %f", index, identity, eventMask, x, y);[/td][/tr]
[tr][td][/td][td]    [/td][/tr]
[tr][td][/td][td]    return _IOHIDEventCreateDigitizerFingerEventWithQuality(allocator, timeStamp, index, identity, eventMask, x, y, z, tipPressure, twist, minorRadius, majorRadius, quality, density, irregularity, range, touch, options);[/td][/tr]
[tr][td][/td][td]}*/[/td][/tr]
[/table]

那就不清楚了,可能是作者用来做测试,发现不是很稳定,就注释掉了吧

已经实现记录功能了,多谢!

1 个赞

分享一下,造福一下大家啊!

在iOS8上使用这个库报Simutale touch failed - -|

这个库现在还不支持iOS 8吧

这库支持iOS8 但是在iPhone6/6+上有问题 上他的源码:

static void SendTouchesEvent(mach_port_t port)
{
      ....
      float factor = 1.0f;
        if (width == 640 || width == 1536) factor = 2.0f;
        
        rX = x/width*factor;
        rY = y/height*factor;

    .....
}

如上源代码,这货居然把factor写死了,死了,了。。。。
因此在旧设备上没问题,在width非640的设备上就有问题咯

请问一下ios8下如果在iphone5c 上simulate touch可以支持?

5c 完美运行 亲测! 6/6+切换到放大显示模式也能正常 标准模式的话就有问题了(坐标差2倍) 不过建议你还是改他源码吧, 在github上面有源码