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


#1

按键精灵,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里去吧!


iOS 10.3模拟点击
#2

实测iOS 7.1下不能用。


#3

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


#4

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


#5

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

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

#6

没在Cydia里装SimulateTouch吧?


#7

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


#8

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


#9

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


#10

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


#11

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


#12

感觉版本有两个吧,上半年我下载到时候有如下的函数:
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]


#13

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


#14

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


#15

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


#16

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


#17

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


#18

这库支持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的设备上就有问题咯


#19

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


#20

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