/SimulateTouch.mm

https://github.com/JatWaston/SimulateTouch · Objective C++ · 367 lines · 244 code · 76 blank · 47 comment · 44 complexity · 249bbe1ef943f9e15314a69c9eb5281f MD5 · raw file

  1. /*
  2. * Name: libSimulateTouch ( kr.iolate.simulatetouch )
  3. * Author: iolate <iolate@me.com>
  4. *
  5. */
  6. #include <substrate.h>
  7. #include <mach/mach.h>
  8. #import <mach/mach_time.h>
  9. #import <CoreGraphics/CoreGraphics.h>
  10. #import <IOKit/hid/IOHIDEvent.h>
  11. #import <IOKit/hid/IOHIDEventSystem.h>
  12. //https://github.com/iolate/iOS-Private-Headers/tree/master/IOKit/hid
  13. #import "private-headers/IOKit/hid/IOHIDEvent7.h"
  14. #import "private-headers/IOKit/hid/IOHIDEventTypes7.h"
  15. #import "private-headers/IOKit/hid/IOHIDEventSystemConnection.h"
  16. #import <rocketbootstrap.h>
  17. #pragma mark - Common declaration
  18. //#define DEBUG
  19. #ifdef DEBUG
  20. # define DLog(...) NSLog(__VA_ARGS__)
  21. #else
  22. # define DLog(...)
  23. #endif
  24. @interface STTouch : NSObject
  25. {
  26. @public
  27. int type; //터치 종류 0: move/stay 1: down 2: up
  28. CGPoint point;
  29. }
  30. @end
  31. @implementation STTouch
  32. @end
  33. static void SendTouchesEvent(mach_port_t port);
  34. static NSMutableDictionary* STTouches = nil; //Dictionary{index:STTouch}
  35. static unsigned int lastPort = 0;
  36. static BOOL iOS7 = NO;
  37. @interface CAWindowServer //in QuartzCore
  38. +(id)serverIfRunning;
  39. -(id)displayWithName:(id)name;
  40. -(NSArray *)displays; //@property(readonly, assign) NSArray* displays;
  41. @end
  42. @interface CAWindowServerDisplay
  43. -(unsigned)clientPortAtPosition:(CGPoint)position;
  44. -(unsigned)contextIdAtPosition:(CGPoint)position;
  45. - (unsigned int)clientPortOfContextId:(unsigned int)arg1;
  46. -(CGRect)bounds;
  47. //iOS7
  48. - (unsigned int)taskPortOfContextId:(unsigned int)arg1; //New!
  49. @end
  50. @interface BKUserEventTimer
  51. + (id)sharedInstance;
  52. - (void)userEventOccurred; //iOS6
  53. - (void)userEventOccurredOnDisplay:(id)arg1; //iOS7
  54. -(BOOL)respondsToSelector:(SEL)selector;
  55. @end
  56. @interface BKHIDSystemInterface
  57. + (id)sharedInstance;
  58. - (void)injectHIDEvent:(IOHIDEventRef)arg1;
  59. @end
  60. @interface BKAccessibility
  61. //IOHIDEventSystemConnectionRef
  62. + (id)_eventRoutingClientConnectionManager;
  63. @end
  64. @interface BKHIDClientConnectionManager
  65. - (IOHIDEventSystemConnectionRef)clientForTaskPort:(unsigned int)arg1;
  66. - (IOHIDEventSystemConnectionRef)clientForBundleID:(id)arg1;
  67. @end
  68. #define Int2String(i) [NSString stringWithFormat:@"%d", i]
  69. #pragma mark - Implementation
  70. /*
  71. MSHook(IOHIDEventRef, IOHIDEventCreateDigitizerEvent, CFAllocatorRef allocator, AbsoluteTime timeStamp, IOHIDDigitizerTransducerType type,
  72. uint32_t index, uint32_t identity, uint32_t eventMask, uint32_t buttonMask,
  73. IOHIDFloat x, IOHIDFloat y, IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat barrelPressure,
  74. Boolean range, Boolean touch, IOOptionBits options) {
  75. //NSLog(@"##### Event %d", type);
  76. //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);
  77. return _IOHIDEventCreateDigitizerEvent(allocator, timeStamp, type, index, identity, eventMask, buttonMask, x, y, z, tipPressure, barrelPressure, range, touch, options);
  78. }
  79. MSHook(IOHIDEventRef, IOHIDEventCreateDigitizerFingerEventWithQuality, CFAllocatorRef allocator, AbsoluteTime timeStamp,
  80. uint32_t index, uint32_t identity, uint32_t eventMask,
  81. IOHIDFloat x, IOHIDFloat y, IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat twist,
  82. IOHIDFloat minorRadius, IOHIDFloat majorRadius, IOHIDFloat quality, IOHIDFloat density, IOHIDFloat irregularity,
  83. Boolean range, Boolean touch, IOOptionBits options) {
  84. //NSLog(@"##### Quality %d %d %d %f %f", index, identity, eventMask, x, y);
  85. return _IOHIDEventCreateDigitizerFingerEventWithQuality(allocator, timeStamp, index, identity, eventMask, x, y, z, tipPressure, twist, minorRadius, majorRadius, quality, density, irregularity, range, touch, options);
  86. }*/
  87. //On iOS6, Symbol not found error because this was added on iOS7
  88. //void IOHIDEventSystemConnectionDispatchEvent(IOHIDEventSystemConnectionRef systemConnection, IOHIDEventRef event) __attribute__((weak));
  89. //MSHook(void, IOHIDEventSystemConnectionDispatchEvent, IOHIDEventSystemConnectionRef systemConnection, IOHIDEventRef event) { }
  90. static void (*_IOHIDEventSystemConnectionDispatchEvent)(IOHIDEventSystemConnectionRef systemConnection, IOHIDEventRef event);
  91. static void hook_IOHIDEventSystemConnectionDispatchEvent(IOHIDEventSystemConnectionRef systemConnection, IOHIDEventRef event) {
  92. //Only for iOS7
  93. if (IOHIDEventSystemConnectionGetType(systemConnection) == 3 && IOHIDEventGetType(event) == 11) {
  94. [STTouches removeAllObjects];
  95. lastPort = 0;
  96. }
  97. _IOHIDEventSystemConnectionDispatchEvent(systemConnection, event);
  98. }
  99. static IOHIDEventSystemCallback original_callback;
  100. static void iohid_event_callback (void* target, void* refcon, IOHIDServiceRef service, IOHIDEventRef event) {
  101. if (IOHIDEventGetType(event) == kIOHIDEventTypeDigitizer) {
  102. [STTouches removeAllObjects];
  103. }
  104. original_callback(target, refcon, service, event);
  105. }
  106. MSHook(Boolean, IOHIDEventSystemOpen, IOHIDEventSystemRef system, IOHIDEventSystemCallback callback, void* target, void* refcon, void* unused) {
  107. original_callback = callback;
  108. return _IOHIDEventSystemOpen(system, iohid_event_callback, target, refcon, unused);
  109. }
  110. static int getExtraIndexNumber()
  111. {
  112. int r = arc4random()%14;
  113. r += 1; //except 0
  114. NSString* pin = Int2String(r);
  115. if ([[STTouches allKeys] containsObject:pin]) {
  116. return getExtraIndexNumber();
  117. }else{
  118. return r;
  119. }
  120. }
  121. static void SimulateTouchEvent(mach_port_t port, int pathIndex, int type, CGPoint touchPoint) {
  122. if (pathIndex == 0) return;
  123. STTouch* touch = [STTouches objectForKey:Int2String(pathIndex)] ?: [[STTouch alloc] init];
  124. touch->type = type;
  125. touch->point = touchPoint;
  126. [STTouches setObject:touch forKey:Int2String(pathIndex)];
  127. SendTouchesEvent(port);
  128. }
  129. static void SendTouchesEvent(mach_port_t port) {
  130. int touchCount = [[STTouches allKeys] count];
  131. if (touchCount == 0) {
  132. return;
  133. }
  134. uint64_t abTime = mach_absolute_time();
  135. AbsoluteTime timeStamp = *(AbsoluteTime *) &abTime;
  136. //iOS6 kIOHIDDigitizerTransducerTypeHand == 35
  137. //iOS7 kIOHIDTransducerTypeHand == 3
  138. IOHIDEventRef handEvent = IOHIDEventCreateDigitizerEvent(kCFAllocatorDefault, timeStamp, iOS7 ? kIOHIDTransducerTypeHand : kIOHIDDigitizerTransducerTypeHand, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  139. //Got on iOS7.
  140. IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerDisplayIntegrated, 1, -268435456); //-268435456
  141. IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldBuiltIn, 1, -268435456); //-268435456
  142. //It looks changing each time, but it doens't care. just don't use 0
  143. #define kIOHIDEventDigitizerSenderID 0x000000010000027F
  144. IOHIDEventSetSenderID(handEvent, kIOHIDEventDigitizerSenderID);
  145. //
  146. int handEventMask = 0;
  147. int handEventTouch = 0;
  148. int touchingCount = 0; //except Up touch
  149. int i = 0;
  150. for (NSString* pIndex in [STTouches allKeys])
  151. {
  152. STTouch* touch = [STTouches objectForKey:pIndex];
  153. int touchType = touch->type;
  154. int eventM = (touchType == 0) ? kIOHIDDigitizerEventPosition : (kIOHIDDigitizerEventRange | kIOHIDDigitizerEventTouch); //Originally, 0, 1 and 2 are used too...
  155. int touch_ = (touchType == 2) ? 0 : 1;
  156. float x = touch->point.x;
  157. float y = touch->point.y;
  158. float rX, rY;
  159. if (iOS7) {
  160. rX = x;
  161. rY = y;
  162. }else{
  163. //0~1 point
  164. id display = [[objc_getClass("CAWindowServer") serverIfRunning] displayWithName:@"LCD"];
  165. CGSize screen = [(CAWindowServerDisplay *)display bounds].size;
  166. //I don't know why, but iPad Air's screen size is {width:2048,height:1536}
  167. float width = MIN(screen.width, screen.height);
  168. float height = MAX(screen.width, screen.height);
  169. float factor = 1.0f;
  170. if (width == 640 || width == 1536) factor = 2.0f;
  171. rX = x/width*factor;
  172. rY = y/height*factor;
  173. }
  174. IOHIDEventRef fingerEvent = IOHIDEventCreateDigitizerFingerEventWithQuality(kCFAllocatorDefault, timeStamp,
  175. [pIndex intValue], i + 2, eventM, rX, rY, 0, 0, 0, 0, 0, 0, 0, 0, touch_, touch_, 0);
  176. IOHIDEventAppendEvent(handEvent, fingerEvent);
  177. i++;
  178. handEventTouch |= touch_;
  179. if (touchType == 0) {
  180. handEventMask |= kIOHIDDigitizerEventPosition; //4
  181. }else{
  182. handEventMask |= (kIOHIDDigitizerEventRange | kIOHIDDigitizerEventTouch | kIOHIDDigitizerEventIdentity); //1 + 2 + 32 = 35
  183. }
  184. if (touchType == 2) {
  185. handEventMask |= kIOHIDDigitizerEventPosition;
  186. [STTouches removeObjectForKey:pIndex];
  187. [touch release];
  188. }else{
  189. touchingCount++;
  190. }
  191. }
  192. //Got on iOS7.
  193. IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerEventMask, handEventMask, -268435456);
  194. IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerRange, handEventTouch, -268435456);
  195. IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerTouch, handEventTouch, -268435456);
  196. //IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerIndex, (1<<22) + (int)pow(2.0, (double)(touchingCount+1)) - 2, -268435456);
  197. BKUserEventTimer* etimer = [NSClassFromString(@"BKUserEventTimer") sharedInstance];
  198. if ([etimer respondsToSelector:@selector(userEventOccurred)]) {
  199. [etimer userEventOccurred];
  200. }else if ([etimer respondsToSelector:@selector(userEventOccurredOnDisplay:)]) {
  201. [etimer userEventOccurredOnDisplay:nil];
  202. }
  203. if (iOS7) {
  204. id manager = [objc_getClass("BKAccessibility") _eventRoutingClientConnectionManager];
  205. IOHIDEventSystemConnectionRef systemConnection = [manager clientForTaskPort:port];
  206. if (systemConnection == nil) return;
  207. _IOHIDEventSystemConnectionDispatchEvent(systemConnection, handEvent);
  208. }else {
  209. original_callback(NULL, NULL, NULL, handEvent);
  210. }
  211. }
  212. #pragma mark - Communicate with Library
  213. typedef struct {
  214. int type;
  215. int index;
  216. float point_x;
  217. float point_y;
  218. } STEvent;
  219. #define POINT(a) CGPointMake(a->point_x, a->point_y)
  220. static CFDataRef messageCallBack(CFMessagePortRef local, SInt32 msgid, CFDataRef cfData, void *info)
  221. {
  222. DLog(@"### ST: Receive Message Id: %d", (int)msgid);
  223. if (msgid == 1) {
  224. if (CFDataGetLength(cfData) == sizeof(STEvent)) {
  225. STEvent* touch = (STEvent *)[(NSData *)cfData bytes];
  226. if (touch != NULL) {
  227. unsigned int port = 0;
  228. if (iOS7) {
  229. id display = [[objc_getClass("CAWindowServer") serverIfRunning] displayWithName:@"LCD"];
  230. unsigned int contextId = [display contextIdAtPosition:POINT(touch)];
  231. port = [display taskPortOfContextId:contextId];
  232. if (lastPort && lastPort != port) {
  233. [STTouches removeAllObjects];
  234. }
  235. lastPort = port;
  236. }
  237. int pathIndex = touch->index;
  238. DLog(@"### ST: Received Path Index: %d", pathIndex);
  239. if (pathIndex == 0) {
  240. pathIndex = getExtraIndexNumber();
  241. }
  242. SimulateTouchEvent(port, pathIndex, touch->type, POINT(touch));
  243. return (CFDataRef)[[NSData alloc] initWithBytes:&pathIndex length:sizeof(pathIndex)];
  244. }else{
  245. DLog(@"### ST: Received STEvent is nil");
  246. return NULL;
  247. }
  248. }
  249. DLog(@"### ST: Received data is not STEvent. event size: %lu received size: %lu", sizeof(STEvent), CFDataGetLength(cfData));
  250. } else {
  251. NSLog(@"### ST: Unknown message type: %d", (int)msgid); //%x
  252. }
  253. return NULL;
  254. }
  255. #pragma mark - MSInitialize
  256. #define MACH_PORT_NAME "kr.iolate.simulatetouch"
  257. #ifdef __cplusplus
  258. extern "C" {
  259. #endif
  260. //Cydia Substrate
  261. typedef const void *MSImageRef;
  262. MSImageRef MSGetImageByName(const char *file);
  263. void *MSFindSymbol(MSImageRef image, const char *name);
  264. #ifdef __cplusplus
  265. }
  266. #endif
  267. MSInitialize {
  268. STTouches = [[NSMutableDictionary alloc] init];
  269. if (objc_getClass("BKHIDSystemInterface")) {
  270. iOS7 = YES;
  271. dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_LAZY);
  272. MSHookFunction(((int *)MSFindSymbol(NULL, "_IOHIDEventSystemConnectionDispatchEvent")), (void *)hook_IOHIDEventSystemConnectionDispatchEvent, (void **)&_IOHIDEventSystemConnectionDispatchEvent);
  273. //MSHookFunction(IOHIDEventSystemConnectionDispatchEvent, MSHake(IOHIDEventSystemConnectionDispatchEvent));
  274. }else{
  275. //iOS6
  276. MSHookFunction(IOHIDEventSystemOpen, MSHake(IOHIDEventSystemOpen));
  277. iOS7 = NO;
  278. }
  279. //MSHookFunction(&IOHIDEventCreateDigitizerEvent, MSHake(IOHIDEventCreateDigitizerEvent));
  280. //MSHookFunction(&IOHIDEventCreateDigitizerFingerEventWithQuality, MSHake(IOHIDEventCreateDigitizerFingerEventWithQuality));
  281. CFMessagePortRef local = CFMessagePortCreateLocal(NULL, CFSTR(MACH_PORT_NAME), messageCallBack, NULL, NULL);
  282. if (rocketbootstrap_cfmessageportexposelocal(local) != 0) {
  283. NSLog(@"### ST: RocketBootstrap failed");
  284. return;
  285. }
  286. CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(NULL, local, 0);
  287. CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
  288. }