├── .gitignore ├── LICENSE ├── README.md ├── WeChatTDylib ├── AntiAntiDebug │ └── AntiAntiDebug.m ├── MethodTrace │ ├── ANYMethodLog.h │ ├── ANYMethodLog.m │ ├── MethodTrace.h │ └── MethodTrace.m ├── ReverseDemoDylib-Prefix.pch ├── ReverseDemoDylib.h ├── ReverseDemoDylib.m ├── ReverseDemoDylib.mm ├── ReverseDemoDylib.xm ├── WeChatHeader.h ├── WeChatHeader.m ├── WeChatSaveData.h ├── WeChatSaveData.m ├── WeChatUser.h ├── WeChatUser.m ├── fishhook │ ├── fishhook.c │ └── fishhook.h └── teachset.jpg ├── images ├── pic1.png ├── pic2.jpeg └── xiaoguo.gif └── podlib └── source ├── .idea ├── inspectionProfiles │ └── Project_Default.xml ├── modules.xml ├── source.iml └── workspace.xml ├── file_catagory.py ├── main.py ├── pod_db.py ├── pod_db.pyc ├── pod_github_file.py ├── pod_github_star.py ├── pod_network.py └── podlib.db /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lefe_x 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 开发APP的另一扇窗--逆向 2 | 3 | 逆向是打开第三方 APP 的一扇窗,本项目主要总结利用逆向做的一些有意义的事情。 4 | 5 | ### 一、给微信添加聊天记录截图功能 6 | 7 | [查看原理](https://github.com/lefex/WeChatShot/wiki/%E7%BB%99%E5%BE%AE%E4%BF%A1%E6%B7%BB%E5%8A%A0%E8%81%8A%E5%A4%A9%E8%AE%B0%E5%BD%95%E6%88%AA%E5%9B%BE%E5%8A%9F%E8%83%BD) 8 | 9 | > 有时候,知识小集群里讨论的技术问题,比较有价值,我们会把有价值的内容整理出来供大家查阅。但为了保护群友隐私,需要把昵称和头像都打码,如果碰到几百条聊天记录,这样做简直要吐血。而且也不能截一张长图,只能一张一张截取,然后拼接起来。群聊记录只能在微信内分享,这也限制了传播的渠道。为了提高小集成员工作效率,想着能不能给微信做个插件,解决这些问题。我们一直在追求如何更有效率做我们的工作,比如使用脚本自动整理每周小集内容,使用微信小程序给读者更好阅读体验。(呀,还有脚本,如果你还不知道,那肯定没有点 star 吧,[传送门](https://github.com/iOS-Tips/iOS-tech-set/tree/master/script)) 10 | 11 | ![](https://github.com/lefex/WeChatShot/blob/master/images/xiaoguo.gif?raw=true) 12 | 13 | [**了解更多微信项目**](https://github.com/lefex/iWeChat) 14 | 15 | ### 二、查看第三方APP使用的第三方库 16 | 17 | > 有时候想研究某个竞品APP时,需要了解其使用的第三方库,使用 class-dump 导出的头文件非常多,刚靠肉眼查看时,耗时耗力。为了解决这个痛点,便发明了这个工具。下面是获取某个第三方 APP 使用的第三方库,可以查看 pod 库的 star 数,源地址。 18 | 19 | - [AFNetworking](https://github.com/AFNetworking/AFNetworking.git) - (30982) 20 | - [Masonry](https://github.com/cloudkite/Masonry.git) - (16318) 21 | - [MJRefresh](https://github.com/CoderMJLee/MJRefresh.git) - (11823) 22 | - [Mantle](https://github.com/github/Mantle.git) - (10984) 23 | - [iCarousel](https://github.com/nicklockwood/iCarousel.git) - (10440) 24 | - [CocoaAsyncSocket](https://github.com/robbiehanson/CocoaAsyncSocket.git) - (9991) 25 | - [YYText](https://github.com/ibireme/YYText.git) - (7245) 26 | - [SWTableViewCell](https://github.com/CEWendel/SWTableViewCell.git) - (7041) 27 | - [Reachability](https://github.com/tonymillion/Reachability.git) - (6626) 28 | - [SSKeychain](https://github.com/soffes/sskeychain.git) - (4808) 29 | - [NJKWebViewProgress](https://github.com/ninjinkun/NJKWebViewProgress.git) - (3863) 30 | - [SSZipArchive](https://github.com/ZipArchive/ZipArchive.git) - (3443) 31 | - [MMWormhole](https://github.com/mutualmobile/MMWormhole.git) - (3414) 32 | - [HMSegmentedControl](https://github.com/HeshamMegid/HMSegmentedControl.git) - (3322) 33 | - [YapDatabase](https://github.com/yaptv/YapDatabase.git) - (3046) 34 | - [hpple](https://github.com/topfunky/hpple.git) - (2534) 35 | - [TYAttributedLabel](https://github.com/12207480/TYAttributedLabel.git) - (2366) 36 | 37 | #### 使用 38 | 39 | 本工具基于 python 写的,在[这里](https://github.com/lefex/WeChatShot/podlib/source)可以找到源码。下载源码后修改 `main.py` 文件的 `IPA_HEADER_PATH` 为 class-dump 导出的头文件目录。执行 `python main.py` 40 | 41 | ``` 42 | IPA_HEADER_PATH = '/Users/lefex/Desktop/header/xxx' 43 | ``` 44 | 45 | 46 | ### 三、分类第三方 APP 头文件 47 | 48 | > 利用 class-dump 导出的头文件,根据前缀整理成不同的文件夹。 49 | 50 | #### 使用 51 | 52 | 本工具基于 python 写的,在[这里](https://github.com/lefex/WeChatShot/podlib/source)可以找到源码。下载源码后修改 `file_catagory.py ` 文件的 `IPA_HEADER_PATH` 为 class-dump 导出的头文件目录。执行 `python file_catagory.py ` 53 | 54 | ``` 55 | IPA_HEADER_PATH = '/Users/lefex/Desktop/header/xxx' 56 | ``` 57 | -------------------------------------------------------------------------------- /WeChatTDylib/AntiAntiDebug/AntiAntiDebug.m: -------------------------------------------------------------------------------- 1 | // weibo: http://weibo.com/xiaoqing28 2 | // blog: http://www.alonemonkey.com 3 | // 4 | // Created by AloneMonkey on 2016/12/10. 5 | // Copyright © 2017年 Coder. All rights reserved. 6 | // 7 | 8 | #if TARGET_OS_SIMULATOR 9 | #error Do not support the simulator, please use the real iPhone Device. 10 | #endif 11 | 12 | #import "fishhook.h" 13 | #import 14 | #import 15 | 16 | typedef int (*ptrace_ptr_t)(int _request,pid_t _pid, caddr_t _addr,int _data); 17 | typedef void* (*dlsym_ptr_t)(void * __handle, const char* __symbol); 18 | typedef int (*syscall_ptr_t)(int, ...); 19 | typedef int (*sysctl_ptr_t)(int *,u_int, void*, size_t*,void*, size_t); 20 | 21 | 22 | static ptrace_ptr_t orig_ptrace = NULL; 23 | static dlsym_ptr_t orig_dlsym = NULL; 24 | static sysctl_ptr_t orig_sysctl = NULL; 25 | static syscall_ptr_t orig_syscall = NULL; 26 | 27 | int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data); 28 | void* my_dlsym(void* __handle, const char* __symbol); 29 | int my_sysctl(int * name, u_int namelen, void * info, size_t * infosize, void * newinfo, size_t newinfosize); 30 | int my_syscall(int code, va_list args); 31 | 32 | int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data){ 33 | if(_request != 31){ 34 | return orig_ptrace(_request,_pid,_addr,_data); 35 | } 36 | 37 | NSLog(@"[AntiAntiDebug] - ptrace request is PT_DENY_ATTACH"); 38 | 39 | return 0; 40 | } 41 | 42 | void* my_dlsym(void* __handle, const char* __symbol){ 43 | if(strcmp(__symbol, "ptrace") != 0){ 44 | return orig_dlsym(__handle, __symbol); 45 | } 46 | 47 | NSLog(@"[AntiAntiDebug] - dlsym get ptrace symbol"); 48 | 49 | return my_ptrace; 50 | } 51 | 52 | typedef struct kinfo_proc _kinfo_proc; 53 | 54 | int my_sysctl(int * name, u_int namelen, void * info, size_t * infosize, void * newinfo, size_t newinfosize){ 55 | if(namelen == 4 && name[0] == CTL_KERN && name[1] == KERN_PROC && name[2] == KERN_PROC_PID && info && infosize && ((int)*infosize == sizeof(_kinfo_proc))){ 56 | int ret = orig_sysctl(name, namelen, info, infosize, newinfo, newinfosize); 57 | struct kinfo_proc *info_ptr = (struct kinfo_proc *)info; 58 | if(info_ptr && (info_ptr->kp_proc.p_flag & P_TRACED) != 0){ 59 | NSLog(@"[AntiAntiDebug] - sysctl query trace status."); 60 | info_ptr->kp_proc.p_flag ^= P_TRACED; 61 | if((info_ptr->kp_proc.p_flag & P_TRACED) == 0){ 62 | NSLog(@"trace status reomve success!"); 63 | } 64 | } 65 | return ret; 66 | } 67 | return orig_sysctl(name, namelen, info, infosize, newinfo, newinfosize); 68 | } 69 | 70 | int my_syscall(int code, va_list args){ 71 | int request; 72 | va_list newArgs; 73 | va_copy(newArgs, args); 74 | if(code == 26){ 75 | #ifdef __LP64__ 76 | __asm__( 77 | "ldr %w[result], [fp, #0x10]\n" 78 | : [result] "=r" (request) 79 | : 80 | : 81 | ); 82 | #else 83 | request = va_arg(args, int); 84 | #endif 85 | if(request == 31){ 86 | NSLog(@"[AntiAntiDebug] - syscall call ptrace, and request is PT_DENY_ATTACH"); 87 | return 0; 88 | } 89 | } 90 | return orig_syscall(code, newArgs); 91 | } 92 | 93 | __attribute__((constructor)) static void entry(){ 94 | NSLog(@"[AntiAntiDebug Init]"); 95 | 96 | rebind_symbols((struct rebinding[1]){{"ptrace", my_ptrace, (void*)&orig_ptrace}},1); 97 | 98 | rebind_symbols((struct rebinding[1]){{"dlsym", my_dlsym, (void*)&orig_dlsym}},1); 99 | 100 | //some app will crash with _dyld_debugger_notification 101 | // rebind_symbols((struct rebinding[1]){{"sysctl", my_sysctl, (void*)&orig_sysctl}},1); 102 | 103 | rebind_symbols((struct rebinding[1]){{"syscall", my_syscall, (void*)&orig_syscall}},1); 104 | } 105 | 106 | -------------------------------------------------------------------------------- /WeChatTDylib/MethodTrace/ANYMethodLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // ANYMethodLog.h 3 | // ANYMethodLog 4 | // 5 | // Created by qiuhaodong on 2017/1/14. 6 | // Copyright © 2017年 qiuhaodong. All rights reserved. 7 | // 8 | // https://github.com/qhd/ANYMethodLog.git 9 | // 10 | 11 | #import 12 | 13 | typedef BOOL (^ConditionBlock)(SEL sel); 14 | typedef void (^BeforeBlock)(id target, SEL sel, NSArray *args, int deep); 15 | typedef void (^AfterBlock)(id target, SEL sel, NSArray *args, NSTimeInterval interval, int deep, id retValue); 16 | 17 | @interface ANYMethodLog : NSObject 18 | 19 | /** 20 | 打印对象的方法调用 21 | 22 | @param aClass 要打印的类 23 | @param condition 根据此 block 来决定是否追踪方法(sel 是方法名) 24 | @param before 方法调用前会调用该 block(target 是检测的对象,sel 是方法名,args 是参数列表) 25 | @param after 方法调用后会调用该 block(interval 是执行方法的耗时) 26 | */ 27 | + (void)logMethodWithClass:(Class)aClass 28 | condition:(ConditionBlock) condition 29 | before:(BeforeBlock) before 30 | after:(AfterBlock) after; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /WeChatTDylib/MethodTrace/ANYMethodLog.m: -------------------------------------------------------------------------------- 1 | // 2 | // ANYMethodLog.m 3 | // ANYMethodLog 4 | // 5 | // Created by qiuhaodong on 2017/1/14. 6 | // Copyright © 2017年 qiuhaodong. All rights reserved. 7 | // 8 | // https://github.com/qhd/ANYMethodLog.git 9 | // 10 | 11 | /*经实践打印出不同类型占的长度,放此以方便调试查看 12 | +---------------------------+------------+------------+--------+ 13 | | type | value(32) | value(64) | comp | 14 | |---------------------------|------------|------------|--------| 15 | | sizeof(char) | 1 | 1 | | 16 | |---------------------------|------------|------------|--------| 17 | | sizeof(int) | 4 | 4 | | 18 | |---------------------------|------------|------------|--------| 19 | | sizeof(short) | 2 | 2 | | 20 | |---------------------------|------------|------------|--------| 21 | | sizeof(long) | 4 | 8 | * | 22 | |---------------------------|------------|------------|--------| 23 | | sizeof(long long) | 8 | 8 | | 24 | |---------------------------|------------|------------|--------| 25 | | sizeof(unsigned char) | 1 | 1 | | 26 | |---------------------------|------------|------------|--------| 27 | | sizeof(unsigned int) | 4 | 4 | | 28 | |---------------------------|------------|------------|--------| 29 | | sizeof(unsigned short) | 2 | 2 | | 30 | |---------------------------|------------|------------|--------| 31 | | sizeof(unsigned long) | 4 | 8 | * | 32 | |---------------------------|------------|------------|--------| 33 | | sizeof(unsigned long long)| 8 | 8 | | 34 | |---------------------------|------------|------------|--------| 35 | | sizeof(float) | 4 | 4 | | 36 | |---------------------------|------------|------------|--------| 37 | | sizeof(double) | 8 | 8 | | 38 | |---------------------------|------------|------------|--------| 39 | | sizeof(BOOL) | 1 | 1 | | 40 | |---------------------------|------------|------------|--------| 41 | | sizeof(void) | 1 | 1 | | 42 | |---------------------------|------------|------------|--------| 43 | | sizeof(char*) | 4 | 8 | * | 44 | |---------------------------|------------|------------|--------| 45 | | sizeof(id) | 4 | 8 | * | 46 | |---------------------------|------------|------------|--------| 47 | | sizeof(Class) | 4 | 8 | * | 48 | |---------------------------|------------|------------|--------| 49 | | sizeof(SEL) | 4 | 8 | * | 50 | +---------------------------+------------+------------+--------+ 51 | */ 52 | 53 | #import "ANYMethodLog.h" 54 | #import 55 | #import 56 | #import 57 | 58 | #pragma mark - deep 59 | 60 | //调用层次 61 | static int deep = -1; 62 | 63 | #pragma mark - Func Define 64 | 65 | BOOL qhd_isInBlackList(NSString *methodName); 66 | NSDictionary *qhd_canHandleTypeDic(void); 67 | BOOL qhd_isCanHandle(NSString *typeEncode); 68 | SEL qhd_createNewSelector(SEL originalSelector); 69 | BOOL qhd_isStructType(const char *argumentType); 70 | NSString *qhd_structName(const char *argumentType); 71 | BOOL isCGRect (const char *type); 72 | BOOL isCGPoint (const char *type); 73 | BOOL isCGSize (const char *type); 74 | BOOL isCGVector (const char *type); 75 | BOOL isUIOffset (const char *type); 76 | BOOL isUIEdgeInsets (const char *type); 77 | BOOL isCGAffineTransform(const char *type); 78 | BOOL qhd_isCanHook(Method method, const char *returnType); 79 | id getReturnValue(NSInvocation *invocation); 80 | NSArray *qhd_method_arguments(NSInvocation *invocation); 81 | void qhd_forwardInvocation(id target, SEL selector, NSInvocation *invocation); 82 | BOOL qhd_replaceMethod(Class cls, SEL originSelector, char *returnType); 83 | void qhd_logMethod(Class aClass, BOOL(^condition)(SEL sel)); 84 | 85 | #pragma mark - AMLBlock 86 | 87 | @interface AMLBlock : NSObject 88 | 89 | @property (strong, nonatomic) NSString *targetClassName; 90 | @property (copy, nonatomic) ConditionBlock condition; 91 | @property (copy, nonatomic) BeforeBlock before; 92 | @property (copy, nonatomic) AfterBlock after; 93 | 94 | @end 95 | 96 | @implementation AMLBlock 97 | 98 | - (BOOL)runCondition:(SEL)sel { 99 | if (self.condition) { 100 | return self.condition(sel); 101 | } else { 102 | return YES; 103 | } 104 | } 105 | 106 | - (void)rundBefore:(id)target sel:(SEL)sel args:(NSArray *)args deep:(int) deep { 107 | if (self.before) { 108 | self.before(target, sel, args, deep); 109 | } 110 | } 111 | 112 | - (void)rundAfter:(id)target sel:(SEL)sel args:(NSArray *)args interval:(NSTimeInterval)interval deep:(int)deep retValue:(id)retValue{ 113 | if (self.after) { 114 | self.after(target, sel, args, interval, deep, retValue); 115 | } 116 | } 117 | 118 | @end 119 | 120 | 121 | #pragma mark - ANYMethodLog private interface 122 | 123 | @interface ANYMethodLog() 124 | 125 | @property (strong, nonatomic) NSMutableDictionary *blockCache; 126 | 127 | + (instancetype)sharedANYMethodLog; 128 | 129 | - (void)setAMLBlock:(AMLBlock *)block forKey:(NSString *)aKey; 130 | 131 | - (AMLBlock *)blockWithTarget:(id)target; 132 | 133 | @end 134 | 135 | 136 | #pragma mark - C function 137 | 138 | #define SHARED_ANYMETHODLOG [ANYMethodLog sharedANYMethodLog] 139 | 140 | //#define OPEN_TARGET_LOG 141 | 142 | #ifdef OPEN_TARGET_LOG 143 | #define TARGET_LOG(format, ...) NSLog(format, ## __VA_ARGS__) 144 | #else 145 | #define TARGET_LOG(format, ...) 146 | #endif 147 | 148 | 149 | //#define OPEN_DEV_LOG 150 | 151 | #ifdef OPEN_DEV_LOG 152 | #define DEV_LOG(format, ...) NSLog(format, ## __VA_ARGS__) 153 | #else 154 | #define DEV_LOG(format, ...) 155 | #endif 156 | 157 | //是否在默认的黑名单中 158 | BOOL qhd_isInBlackList(NSString *methodName) { 159 | static NSArray *defaultBlackList = nil; 160 | static dispatch_once_t onceToken; 161 | dispatch_once(&onceToken, ^{ 162 | defaultBlackList = @[/*UIViewController的:*/@".cxx_destruct", @"dealloc", @"_isDeallocating", @"release", @"autorelease", @"retain", @"Retain", @"_tryRetain", @"copy", /*UIView的:*/ @"nsis_descriptionOfVariable:", /*NSObject的:*/@"respondsToSelector:", @"class", @"methodSignatureForSelector:", @"allowsWeakReference", @"retainWeakReference", @"init", @"forwardInvocation:"]; 163 | }); 164 | return ([defaultBlackList containsObject:methodName]); 165 | } 166 | 167 | /*reference: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1 168 | 经实践发现与文档有差别 169 | 1.在64位时@encode(long)跟@encode(long long)的值一样; 170 | 2.在64位时@encode(unsigned long)跟@encode(unsigned long long)的值一样; 171 | 3.在32位时@encode(BOOL)跟@encode(char)一样。 172 | +--------------------+-----------+-----------+ 173 | | type |code(32bit)|code(64bit)| 174 | |--------------------|-----------|-----------| 175 | | BOOL | c | B | 176 | |--------------------|-----------|-----------| 177 | | char | c | c | 178 | |--------------------|-----------|-----------| 179 | | long | l | q | 180 | |--------------------|-----------|-----------| 181 | | long long | q | q | 182 | |--------------------|-----------|-----------| 183 | | unsigned long | L | Q | 184 | |--------------------|-----------|-----------| 185 | | unsigned long long | Q | Q | 186 | +--------------------+-----------+-----------+ 187 | */ 188 | NSDictionary *qhd_canHandleTypeDic() { 189 | static NSDictionary *dic = nil; 190 | static dispatch_once_t onceToken; 191 | dispatch_once(&onceToken, ^{ 192 | dic = @{[NSString stringWithUTF8String:@encode(char)] : @"(char)", 193 | [NSString stringWithUTF8String:@encode(int)] : @"(int)", 194 | [NSString stringWithUTF8String:@encode(short)] : @"(short)", 195 | [NSString stringWithUTF8String:@encode(long)] : @"(long)", 196 | [NSString stringWithUTF8String:@encode(long long)] : @"(long long)", 197 | [NSString stringWithUTF8String:@encode(unsigned char)] : @"(unsigned char))", 198 | [NSString stringWithUTF8String:@encode(unsigned int)] : @"(unsigned int)", 199 | [NSString stringWithUTF8String:@encode(unsigned short)] : @"(unsigned short)", 200 | [NSString stringWithUTF8String:@encode(unsigned long)] : @"(unsigned long)", 201 | [NSString stringWithUTF8String:@encode(unsigned long long)] : @"(unsigned long long)", 202 | [NSString stringWithUTF8String:@encode(float)] : @"(float)", 203 | [NSString stringWithUTF8String:@encode(double)] : @"(double)", 204 | [NSString stringWithUTF8String:@encode(BOOL)] : @"(BOOL)", 205 | [NSString stringWithUTF8String:@encode(void)] : @"(void)", 206 | [NSString stringWithUTF8String:@encode(char *)] : @"(char *)", 207 | [NSString stringWithUTF8String:@encode(id)] : @"(id)", 208 | [NSString stringWithUTF8String:@encode(Class)] : @"(Class)", 209 | [NSString stringWithUTF8String:@encode(SEL)] : @"(SEL)", 210 | [NSString stringWithUTF8String:@encode(CGRect)] : @"(CGRect)", 211 | [NSString stringWithUTF8String:@encode(CGPoint)] : @"(CGPoint)", 212 | [NSString stringWithUTF8String:@encode(CGSize)] : @"(CGSize)", 213 | [NSString stringWithUTF8String:@encode(CGVector)] : @"(CGVector)", 214 | [NSString stringWithUTF8String:@encode(CGAffineTransform)] : @"(CGAffineTransform)", 215 | [NSString stringWithUTF8String:@encode(UIOffset)] : @"(UIOffset)", 216 | [NSString stringWithUTF8String:@encode(UIEdgeInsets)] : @"(UIEdgeInsets)", 217 | @"@?":@"(block)" // block类型 218 | };//TODO:添加其他类型 219 | }); 220 | return dic; 221 | } 222 | 223 | //根据定义的类型的判断是否能处理 224 | BOOL qhd_isCanHandle(NSString *typeEncode) { 225 | return [qhd_canHandleTypeDic().allKeys containsObject:typeEncode]; 226 | } 227 | 228 | //创建一个新的selector 229 | SEL qhd_createNewSelector(SEL originalSelector) { 230 | NSString *oldSelectorName = NSStringFromSelector(originalSelector); 231 | NSString *newSelectorName = [NSString stringWithFormat:@"qhd_%@", oldSelectorName]; 232 | SEL newSelector = NSSelectorFromString(newSelectorName); 233 | return newSelector; 234 | } 235 | 236 | //是否struct类型 237 | BOOL qhd_isStructType(const char *argumentType) { 238 | NSString *typeString = [NSString stringWithUTF8String:argumentType]; 239 | return ([typeString hasPrefix:@"{"] && [typeString hasSuffix:@"}"]); 240 | } 241 | 242 | //struct类型名 243 | NSString *qhd_structName(const char *argumentType) { 244 | NSString *typeString = [NSString stringWithUTF8String:argumentType]; 245 | NSUInteger start = [typeString rangeOfString:@"{"].location; 246 | NSUInteger end = [typeString rangeOfString:@"="].location; 247 | if (end > start) { 248 | return [typeString substringWithRange:NSMakeRange(start + 1, end - start - 1)]; 249 | } else { 250 | return nil; 251 | } 252 | } 253 | 254 | BOOL isCGRect (const char *type) {return [qhd_structName(type) isEqualToString:@"CGRect"];} 255 | BOOL isCGPoint (const char *type) {return [qhd_structName(type) isEqualToString:@"CGPoint"];} 256 | BOOL isCGSize (const char *type) {return [qhd_structName(type) isEqualToString:@"CGSize"];} 257 | BOOL isCGVector (const char *type) {return [qhd_structName(type) isEqualToString:@"CGVector"];} 258 | BOOL isUIOffset (const char *type) {return [qhd_structName(type) isEqualToString:@"UIOffset"];} 259 | BOOL isUIEdgeInsets (const char *type) {return [qhd_structName(type) isEqualToString:@"UIEdgeInsets"];} 260 | BOOL isCGAffineTransform(const char *type) {return [qhd_structName(type) isEqualToString:@"CGAffineTransform"];} 261 | 262 | //检查是否能处理 263 | BOOL qhd_isCanHook(Method method, const char *returnType) { 264 | 265 | //若在黑名单中则不处理 266 | NSString *selectorName = NSStringFromSelector(method_getName(method)); 267 | if (qhd_isInBlackList(selectorName)) { 268 | return NO; 269 | } 270 | 271 | if ([selectorName rangeOfString:@"qhd_"].location != NSNotFound) { 272 | return NO; 273 | } 274 | 275 | NSString *returnTypeString = [NSString stringWithUTF8String:returnType]; 276 | 277 | BOOL isCanHook = YES; 278 | if (!qhd_isCanHandle(returnTypeString)) { 279 | isCanHook = NO; 280 | } 281 | for(int k = 2 ; k < method_getNumberOfArguments(method); k ++) { 282 | char argument[250]; 283 | memset(argument, 0, sizeof(argument)); 284 | method_getArgumentType(method, k, argument, sizeof(argument)); 285 | NSString *argumentString = [NSString stringWithUTF8String:argument]; 286 | if (!qhd_isCanHandle(argumentString)) { 287 | isCanHook = NO; 288 | break; 289 | } 290 | } 291 | return isCanHook; 292 | } 293 | 294 | //获取方法返回值 295 | id getReturnValue(NSInvocation *invocation){ 296 | const char *returnType = invocation.methodSignature.methodReturnType; 297 | if (returnType[0] == 'r') { 298 | returnType++; 299 | } 300 | #define WRAP_GET_VALUE(type) \ 301 | do { \ 302 | type val = 0; \ 303 | [invocation getReturnValue:&val]; \ 304 | return @(val); \ 305 | } while (0) 306 | if (strcmp(returnType, @encode(id)) == 0 || strcmp(returnType, @encode(Class)) == 0 || strcmp(returnType, @encode(void (^)(void))) == 0) { 307 | __autoreleasing id returnObj; 308 | [invocation getReturnValue:&returnObj]; 309 | return returnObj; 310 | } else if (strcmp(returnType, @encode(char)) == 0) { 311 | WRAP_GET_VALUE(char); 312 | } else if (strcmp(returnType, @encode(int)) == 0) { 313 | WRAP_GET_VALUE(int); 314 | } else if (strcmp(returnType, @encode(short)) == 0) { 315 | WRAP_GET_VALUE(short); 316 | } else if (strcmp(returnType, @encode(long)) == 0) { 317 | WRAP_GET_VALUE(long); 318 | } else if (strcmp(returnType, @encode(long long)) == 0) { 319 | WRAP_GET_VALUE(long long); 320 | } else if (strcmp(returnType, @encode(unsigned char)) == 0) { 321 | WRAP_GET_VALUE(unsigned char); 322 | } else if (strcmp(returnType, @encode(unsigned int)) == 0) { 323 | WRAP_GET_VALUE(unsigned int); 324 | } else if (strcmp(returnType, @encode(unsigned short)) == 0) { 325 | WRAP_GET_VALUE(unsigned short); 326 | } else if (strcmp(returnType, @encode(unsigned long)) == 0) { 327 | WRAP_GET_VALUE(unsigned long); 328 | } else if (strcmp(returnType, @encode(unsigned long long)) == 0) { 329 | WRAP_GET_VALUE(unsigned long long); 330 | } else if (strcmp(returnType, @encode(float)) == 0) { 331 | WRAP_GET_VALUE(float); 332 | } else if (strcmp(returnType, @encode(double)) == 0) { 333 | WRAP_GET_VALUE(double); 334 | } else if (strcmp(returnType, @encode(BOOL)) == 0) { 335 | WRAP_GET_VALUE(BOOL); 336 | } else if (strcmp(returnType, @encode(char *)) == 0) { 337 | WRAP_GET_VALUE(const char *); 338 | } else if (strcmp(returnType, @encode(void)) == 0) { 339 | return @"void"; 340 | } else { 341 | NSUInteger valueSize = 0; 342 | NSGetSizeAndAlignment(returnType, &valueSize, NULL); 343 | unsigned char valueBytes[valueSize]; 344 | [invocation getReturnValue:valueBytes]; 345 | 346 | return [NSValue valueWithBytes:valueBytes objCType:returnType]; 347 | } 348 | return nil; 349 | } 350 | 351 | //获取方法参数 352 | NSArray *qhd_method_arguments(NSInvocation *invocation) { 353 | NSMethodSignature *methodSignature = [invocation methodSignature]; 354 | NSMutableArray *argList = (methodSignature.numberOfArguments > 2 ? [NSMutableArray array] : nil); 355 | for (NSUInteger i = 2; i < methodSignature.numberOfArguments; i++) { 356 | const char *argumentType = [methodSignature getArgumentTypeAtIndex:i]; 357 | id arg = nil; 358 | 359 | if (qhd_isStructType(argumentType)) { 360 | #define GET_STRUCT_ARGUMENT(_type)\ 361 | if (is##_type(argumentType)) {\ 362 | _type arg_temp;\ 363 | [invocation getArgument:&arg_temp atIndex:i];\ 364 | arg = NSStringFrom##_type(arg_temp);\ 365 | } 366 | GET_STRUCT_ARGUMENT(CGRect) 367 | else GET_STRUCT_ARGUMENT(CGPoint) 368 | else GET_STRUCT_ARGUMENT(CGSize) 369 | else GET_STRUCT_ARGUMENT(CGVector) 370 | else GET_STRUCT_ARGUMENT(UIOffset) 371 | else GET_STRUCT_ARGUMENT(UIEdgeInsets) 372 | else GET_STRUCT_ARGUMENT(CGAffineTransform) 373 | 374 | if (arg == nil) { 375 | arg = @"{unknown}"; 376 | } 377 | } 378 | #define GET_ARGUMENT(_type)\ 379 | if (0 == strcmp(argumentType, @encode(_type))) {\ 380 | _type arg_temp;\ 381 | [invocation getArgument:&arg_temp atIndex:i];\ 382 | arg = @(arg_temp);\ 383 | } 384 | else GET_ARGUMENT(char) 385 | else GET_ARGUMENT(int) 386 | else GET_ARGUMENT(short) 387 | else GET_ARGUMENT(long) 388 | else GET_ARGUMENT(long long) 389 | else GET_ARGUMENT(unsigned char) 390 | else GET_ARGUMENT(unsigned int) 391 | else GET_ARGUMENT(unsigned short) 392 | else GET_ARGUMENT(unsigned long) 393 | else GET_ARGUMENT(unsigned long long) 394 | else GET_ARGUMENT(float) 395 | else GET_ARGUMENT(double) 396 | else GET_ARGUMENT(BOOL) 397 | else if (0 == strcmp(argumentType, @encode(id))) { 398 | __unsafe_unretained id arg_temp; 399 | [invocation getArgument:&arg_temp atIndex:i]; 400 | arg = arg_temp; 401 | } 402 | else if (0 == strcmp(argumentType, @encode(SEL))) { 403 | SEL arg_temp; 404 | [invocation getArgument:&arg_temp atIndex:i]; 405 | arg = NSStringFromSelector(arg_temp); 406 | } 407 | else if (0 == strcmp(argumentType, @encode(char *))) { 408 | char *arg_temp; 409 | [invocation getArgument:&arg_temp atIndex:i]; 410 | arg = [NSString stringWithUTF8String:arg_temp]; 411 | } 412 | else if (0 == strcmp(argumentType, @encode(void *))) { 413 | void *arg_temp; 414 | [invocation getArgument:&arg_temp atIndex:i]; 415 | arg = (__bridge id _Nonnull)arg_temp; 416 | } 417 | else if (0 == strcmp(argumentType, @encode(Class))) { 418 | Class arg_temp; 419 | [invocation getArgument:&arg_temp atIndex:i]; 420 | arg = arg_temp; 421 | } 422 | 423 | if (!arg) { 424 | arg = @"unknown"; 425 | } 426 | [argList addObject:arg]; 427 | } 428 | return argList; 429 | } 430 | 431 | //forwardInvocation:方法的新IMP 432 | void qhd_forwardInvocation(id target, SEL selector, NSInvocation *invocation) { 433 | NSArray *argList = qhd_method_arguments(invocation); 434 | 435 | SEL originSelector = invocation.selector; 436 | 437 | NSString *originSelectorString = NSStringFromSelector(originSelector); 438 | 439 | //友盟的UMAOCTools会产生问题 440 | if ([originSelectorString rangeOfString:@"hook_"].location != NSNotFound) { 441 | return; 442 | } 443 | 444 | [invocation setSelector:qhd_createNewSelector(originSelector)]; 445 | [invocation setTarget:target]; 446 | 447 | deep++; 448 | 449 | AMLBlock *block = [SHARED_ANYMETHODLOG blockWithTarget:target]; 450 | [block rundBefore:target sel:originSelector args:argList deep:deep]; 451 | 452 | NSDate *start = [NSDate date]; 453 | 454 | [invocation invoke]; 455 | 456 | NSDate *end = [NSDate date]; 457 | NSTimeInterval interval = [end timeIntervalSinceDate:start]; 458 | 459 | [block rundAfter:target sel:originSelector args:argList interval:interval deep:deep retValue:getReturnValue(invocation)]; 460 | 461 | deep--; 462 | } 463 | 464 | //替换方法 465 | BOOL qhd_replaceMethod(Class cls, SEL originSelector, char *returnType) { 466 | Method originMethod = class_getInstanceMethod(cls, originSelector); 467 | if (originMethod == nil) { 468 | return NO; 469 | } 470 | const char *originTypes = method_getTypeEncoding(originMethod); 471 | IMP msgForwardIMP = _objc_msgForward; 472 | #if !defined(__arm64__) 473 | if (qhd_isStructType(returnType)) { 474 | //Reference JSPatch: 475 | //In some cases that returns struct, we should use the '_stret' API: 476 | //http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html 477 | //NSMethodSignature knows the detail but has no API to return, we can only get the info from debugDescription. 478 | NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:originTypes]; 479 | if ([methodSignature.debugDescription rangeOfString:@"is special struct return? YES"].location != NSNotFound) { 480 | msgForwardIMP = (IMP)_objc_msgForward_stret; 481 | } 482 | } 483 | #endif 484 | 485 | IMP originIMP = method_getImplementation(originMethod); 486 | 487 | if (originIMP == nil || originIMP == msgForwardIMP) { 488 | return NO; 489 | } 490 | 491 | //把原方法的IMP换成_objc_msgForward,使之触发forwardInvocation方法 492 | class_replaceMethod(cls, originSelector, msgForwardIMP, originTypes); 493 | 494 | //把方法forwardInvocation的IMP换成qhd_forwardInvocation 495 | class_replaceMethod(cls, @selector(forwardInvocation:), (IMP)qhd_forwardInvocation, "v@:@"); 496 | 497 | //创建一个新方法,IMP就是原方法的原来的IMP,那么只要在qhd_forwardInvocation调用新方法即可 498 | SEL newSelecotr = qhd_createNewSelector(originSelector); 499 | BOOL isAdd = class_addMethod(cls, newSelecotr, originIMP, originTypes); 500 | if (!isAdd) { 501 | DEV_LOG(@"class_addMethod fail"); 502 | } 503 | 504 | return YES; 505 | } 506 | 507 | void qhd_logMethod(Class aClass, BOOL(^condition)(SEL sel)) { 508 | unsigned int outCount; 509 | Method *methods = class_copyMethodList(aClass,&outCount); 510 | 511 | for (int i = 0; i < outCount; i ++) { 512 | Method tempMethod = *(methods + i); 513 | SEL selector = method_getName(tempMethod); 514 | char *returnType = method_copyReturnType(tempMethod); 515 | 516 | BOOL isCan = qhd_isCanHook(tempMethod, returnType); 517 | 518 | if (isCan && condition) { 519 | isCan = condition(selector); 520 | } 521 | 522 | if (isCan) { 523 | if (qhd_replaceMethod(aClass, selector, returnType)) { 524 | DEV_LOG(@"success hook method:%@ types:%s", NSStringFromSelector(selector), method_getDescription(tempMethod)->types); 525 | } else { 526 | DEV_LOG(@"fail method:%@ types:%s", NSStringFromSelector(selector), method_getDescription(tempMethod)->types); 527 | } 528 | } else { 529 | DEV_LOG(@"can not hook method:%@ types:%s", NSStringFromSelector(selector), method_getDescription(tempMethod)->types); 530 | } 531 | free(returnType); 532 | } 533 | free(methods); 534 | } 535 | 536 | 537 | #pragma mark - ANYMethodLog implementation 538 | 539 | @implementation ANYMethodLog 540 | 541 | + (void)logMethodWithClass:(Class)aClass 542 | condition:(ConditionBlock) condition 543 | before:(BeforeBlock) before 544 | after:(AfterBlock) after { 545 | #ifndef DEBUG 546 | return; 547 | #endif 548 | 549 | if (aClass) { 550 | AMLBlock *block = [[AMLBlock alloc] init]; 551 | block.targetClassName = NSStringFromClass(aClass); 552 | block.condition = condition; 553 | block.before = before; 554 | block.after = after; 555 | [SHARED_ANYMETHODLOG setAMLBlock:block forKey:block.targetClassName]; 556 | } 557 | 558 | qhd_logMethod(aClass, condition); 559 | 560 | //获取元类,处理类方法。(注意获取元类是用object_getClass,而不是class_getSuperclass) 561 | Class metaClass = object_getClass(aClass); 562 | qhd_logMethod(metaClass, condition); 563 | } 564 | 565 | + (instancetype)sharedANYMethodLog { 566 | static ANYMethodLog *_sharedANYMethodLog = nil; 567 | static dispatch_once_t onceToken; 568 | dispatch_once(&onceToken, ^{ 569 | _sharedANYMethodLog = [[self alloc] init]; 570 | _sharedANYMethodLog.blockCache = [NSMutableDictionary dictionary]; 571 | }); 572 | return _sharedANYMethodLog; 573 | } 574 | 575 | - (void)setAMLBlock:(AMLBlock *)block forKey:(NSString *)aKey { 576 | @synchronized (self) { 577 | [self.blockCache setObject:block forKey:aKey]; 578 | } 579 | } 580 | 581 | - (AMLBlock *)blockWithTarget:(id)target { 582 | Class class = [target class]; 583 | AMLBlock *block = [self.blockCache objectForKey:NSStringFromClass(class)]; 584 | while (block == nil) { 585 | class = [class superclass]; 586 | if (class == nil) { 587 | break; 588 | } 589 | block = [self.blockCache objectForKey:NSStringFromClass(class)]; 590 | } 591 | return block; 592 | } 593 | 594 | @end 595 | -------------------------------------------------------------------------------- /WeChatTDylib/MethodTrace/MethodTrace.h: -------------------------------------------------------------------------------- 1 | // weibo: http://weibo.com/xiaoqing28 2 | // blog: http://www.alonemonkey.com 3 | // 4 | // Created by AloneMonkey on 2017/9/7. 5 | // Copyright © 2017年 AloneMonkey. All rights reserved. 6 | // 7 | 8 | #ifndef MethodTrace_h 9 | #define MethodTrace_h 10 | 11 | #import 12 | 13 | @interface MethodTrace : NSObject 14 | 15 | + (void)addClassTrace:(NSString*) className; 16 | 17 | + (void)addClassTrace:(NSString *)className methodName:(NSString*) methodName; 18 | 19 | + (void)addClassTrace:(NSString *)className methodList:(NSArray*) methodList; 20 | 21 | @end 22 | 23 | #endif /* MethodTrace_h */ 24 | -------------------------------------------------------------------------------- /WeChatTDylib/MethodTrace/MethodTrace.m: -------------------------------------------------------------------------------- 1 | // weibo: http://weibo.com/xiaoqing28 2 | // blog: http://www.alonemonkey.com 3 | // 4 | // Created by AloneMonkey on 2017/9/6. 5 | // Copyright © 2017年 AloneMonkey. All rights reserved. 6 | // 7 | 8 | #import "ANYMethodLog.h" 9 | #import "MethodTrace.h" 10 | #import 11 | #import 12 | 13 | @implementation MethodTrace : NSObject 14 | 15 | +(void)addClassTrace:(NSString *)className{ 16 | [self addClassTrace:className methodList:nil]; 17 | } 18 | 19 | +(void)addClassTrace:(NSString *)className methodName:(NSString *)methodName{ 20 | [self addClassTrace:className methodList:@[methodName]]; 21 | } 22 | 23 | +(void)addClassTrace:(NSString *)className methodList:(NSArray *)methodList{ 24 | Class targetClass = objc_getClass([className UTF8String]); 25 | if(targetClass != nil){ 26 | [ANYMethodLog logMethodWithClass:NSClassFromString(className) condition:^BOOL(SEL sel) { 27 | return (methodList == nil || methodList.count == 0) ? YES : [methodList containsObject:NSStringFromSelector(sel)]; 28 | } before:^(id target, SEL sel, NSArray *args, int deep) { 29 | NSString *selector = NSStringFromSelector(sel); 30 | NSArray *selectorArrary = [selector componentsSeparatedByString:@":"]; 31 | selectorArrary = [selectorArrary filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"length > 0"]]; 32 | NSMutableString *selectorString = [NSMutableString new]; 33 | for (int i = 0; i < selectorArrary.count; i++) { 34 | [selectorString appendFormat:@"%@:%@ ", selectorArrary[i], args[i]]; 35 | } 36 | NSMutableString *deepString = [NSMutableString new]; 37 | for (int i = 0; i < deep; i++) { 38 | [deepString appendString:@"-"]; 39 | } 40 | NSLog(@"%@[%@ %@]", deepString , target, selectorString); 41 | } after:^(id target, SEL sel, NSArray *args, NSTimeInterval interval,int deep, id retValue) { 42 | NSMutableString *deepString = [NSMutableString new]; 43 | for (int i = 0; i < deep; i++) { 44 | [deepString appendString:@"-"]; 45 | } 46 | NSLog(@"%@ret:%@", deepString, retValue); 47 | }]; 48 | }else{ 49 | NSLog(@"canot find class %@", className); 50 | } 51 | } 52 | 53 | @end 54 | 55 | static __attribute__((constructor)) void entry(){ 56 | NSString* configFilePath = [[NSBundle mainBundle] pathForResource:@"MethodTraceConfig" ofType:@"plist"]; 57 | if(configFilePath == nil){ 58 | NSLog(@"MethodTraceConfig.plist file is not exits!!!"); 59 | return; 60 | } 61 | NSMutableDictionary *configItem = [NSMutableDictionary dictionaryWithContentsOfFile:configFilePath]; 62 | BOOL isEnable = [[configItem valueForKey:@"ENABLE_METHODTRACE"] boolValue]; 63 | if(isEnable){ 64 | NSDictionary* classListDictionary = [configItem valueForKey:@"TARGET_CLASS_LIST"]; 65 | for (NSString* className in classListDictionary.allKeys) { 66 | Class targetClass = objc_getClass([className UTF8String]); 67 | if(targetClass != nil){ 68 | id methodList = [classListDictionary valueForKey:className]; 69 | if([methodList isKindOfClass:[NSArray class]]){ 70 | [MethodTrace addClassTrace:className methodList:methodList]; 71 | }else{ 72 | [MethodTrace addClassTrace:className]; 73 | } 74 | }else{ 75 | NSLog(@"canot find class %@", className); 76 | } 77 | } 78 | }else{ 79 | NSLog(@"Method Trace is disable"); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /WeChatTDylib/ReverseDemoDylib-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ReverseDemoDylib' target in the 'ReverseDemoDylib' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import "/opt/theos/Prefix.pch" //path/to/theos/Prefix.pch 8 | #endif 9 | -------------------------------------------------------------------------------- /WeChatTDylib/ReverseDemoDylib.h: -------------------------------------------------------------------------------- 1 | // weibo: http://weibo.com/xiaoqing28 2 | // blog: http://www.alonemonkey.com 3 | // 4 | // ReverseDemoDylib.h 5 | // ReverseDemoDylib 6 | // 7 | // Created by Wang,Suyan on 2018/1/10. 8 | // Copyright (c) 2018年 Wang,Suyan. All rights reserved. 9 | // 10 | 11 | #import 12 | 13 | 14 | -------------------------------------------------------------------------------- /WeChatTDylib/ReverseDemoDylib.m: -------------------------------------------------------------------------------- 1 | // weibo: http://weibo.com/xiaoqing28 2 | // blog: http://www.alonemonkey.com 3 | // 4 | // ReverseDemoDylib.m 5 | // ReverseDemoDylib 6 | // 7 | // Created by Wang,Suyan on 2018/1/10. 8 | // Copyright (c) 2018年 Wang,Suyan. All rights reserved. 9 | // 10 | 11 | #import "ReverseDemoDylib.h" 12 | #import 13 | #import 14 | #import 15 | #import "ANYMethodLog.h" 16 | #import "WeChatHeader.h" 17 | #import "WeChatSaveData.h" 18 | 19 | static __attribute__((constructor)) void entry(){ 20 | NSLog(@"\n 🎉!!!congratulations!!!🎉\n👍----------------insert dylib success----------------👍"); 21 | 22 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { 23 | 24 | CYListenServer(6666); 25 | }]; 26 | } 27 | 28 | #pragma mark - WeChatActionSheet 29 | 30 | CHDeclareClass(WCActionSheet) 31 | CHOptimizedMethod0(self, NSArray *, WCActionSheet, buttonTitleList){ 32 | NSArray *titles = CHSuper0(WCActionSheet, buttonTitleList); 33 | // NSLog(@"WCActionSheet titles: %@, class: %@", titles, NSStringFromClass([[titles firstObject] class])); 34 | return titles; 35 | } 36 | 37 | CHOptimizedMethod1(self, void, WCActionSheet, showInView, UIView *, view){ 38 | if ([WeChatSaveData defaultSaveData].isNeedAddMenu) { 39 | [self addButtonWithTitle:@""]; 40 | [self addButtonWithTitle:kScreenshotTitle]; 41 | [self addButtonWithTitle:kScreenshotTitleMask]; 42 | [self addButtonWithTitle:kScreenshotTitlePreview]; 43 | 44 | // 方法二 45 | // WCActionSheetItem *shotItem = [[objc_getClass("WCActionSheetItem") alloc] initWithTitle:kScreenshotTitle]; 46 | // WCActionSheetItem *shotItem2 = [[objc_getClass("WCActionSheetItem") alloc] initWithTitle:kScreenshotTitleMask]; 47 | // [self.buttonTitleList addObject:shotItem]; 48 | // [self.buttonTitleList addObject:shotItem2]; 49 | } 50 | CHSuper1(WCActionSheet, showInView, view); 51 | } 52 | 53 | CHDeclareClass(WCActionSheetItem); 54 | // 不点击菜单按钮时,点击取消或者其它空白的地方时 index 一直为最后加的菜单对应的 index,这里加个空白的 55 | CHOptimizedMethod0(self, double, WCActionSheetItem, getItemHeight){ 56 | double heigth = CHSuper0(WCActionSheetItem, getItemHeight); 57 | if(self.title.length == 0) { 58 | return 0; 59 | } 60 | return heigth; 61 | } 62 | 63 | #pragma mark - 收藏消息详情 64 | 65 | CHDeclareClass(FavRecordDetailViewController) 66 | CHOptimizedMethod2(self, void, FavRecordDetailViewController, actionSheet, WCActionSheet*, sheet, clickedButtonAtIndex, int, index){ 67 | NSLog(@"Hook clickedButtonAtIndex"); 68 | 69 | CHSuper2(FavRecordDetailViewController, actionSheet, sheet, clickedButtonAtIndex, index); 70 | 71 | [WeChatCapture saveCaptureImageWithSheet:sheet index:index viewController:self]; 72 | } 73 | 74 | CHOptimizedMethod2(self, UITableViewCell *, FavRecordDetailViewController, tableView, MMTableView *, tableViewArg, cellForRowAtIndexPath, NSIndexPath, *indexPath){ 75 | 76 | UITableViewCell *cell = CHSuper2(FavRecordDetailViewController, tableView, tableViewArg, cellForRowAtIndexPath, indexPath); 77 | [WeChatCapture updateCellDataWithCell:cell indexPath:indexPath]; 78 | 79 | return cell; 80 | } 81 | 82 | CHOptimizedMethod1(self, void, FavRecordDetailViewController, viewWillAppear, BOOL, arg){ 83 | CHSuper1(FavRecordDetailViewController, viewWillAppear, arg); 84 | [WeChatCapture viewWillAppearAction]; 85 | } 86 | 87 | CHOptimizedMethod1(self, void, FavRecordDetailViewController, viewWillDisappear, BOOL, arg){ 88 | CHSuper1(FavRecordDetailViewController, viewWillDisappear, arg); 89 | [WeChatCapture viewWillDisappearAction]; 90 | } 91 | 92 | #pragma mark - 消息记录 93 | 94 | CHDeclareClass(MsgRecordDetailViewController); 95 | CHOptimizedMethod1(self, void, MsgRecordDetailViewController, viewWillAppear, BOOL, arg){ 96 | CHSuper1(MsgRecordDetailViewController, viewWillAppear, arg); 97 | [WeChatCapture viewWillAppearAction]; 98 | } 99 | 100 | CHOptimizedMethod1(self, void, MsgRecordDetailViewController, viewWillDisappear, BOOL, arg){ 101 | CHSuper1(MsgRecordDetailViewController, viewWillDisappear, arg); 102 | [WeChatCapture viewWillDisappearAction]; 103 | } 104 | 105 | CHOptimizedMethod2(self, void, MsgRecordDetailViewController, actionSheet, WCActionSheet*, sheet, clickedButtonAtIndex, int, index){ 106 | NSLog(@"Hook clickedButtonAtIndex"); 107 | 108 | CHSuper2(MsgRecordDetailViewController, actionSheet, sheet, clickedButtonAtIndex, index); 109 | 110 | [WeChatCapture saveCaptureImageWithSheet:sheet index:index viewController:self]; 111 | } 112 | 113 | CHOptimizedMethod2(self, UITableViewCell *, MsgRecordDetailViewController, tableView, MMTableView *, tableViewArg, cellForRowAtIndexPath, NSIndexPath, *indexPath){ 114 | 115 | UITableViewCell *cell = CHSuper2(MsgRecordDetailViewController, tableView, tableViewArg, cellForRowAtIndexPath, indexPath); 116 | [WeChatCapture updateCellDataWithCell:cell indexPath:indexPath]; 117 | 118 | return cell; 119 | } 120 | 121 | #pragma mark - 构造 122 | 123 | CHConstructor{ 124 | CHLoadLateClass(WCActionSheet); 125 | CHClassHook0(WCActionSheet, buttonTitleList); 126 | CHClassHook1(WCActionSheet, showInView); 127 | 128 | CHLoadLateClass(WCActionSheetItem); 129 | CHClassHook0(WCActionSheetItem, getItemHeight); 130 | 131 | CHLoadLateClass(FavRecordDetailViewController); 132 | CHClassHook2(FavRecordDetailViewController, actionSheet, clickedButtonAtIndex); 133 | CHClassHook2(FavRecordDetailViewController, tableView, cellForRowAtIndexPath); 134 | CHClassHook1(FavRecordDetailViewController, viewWillAppear); 135 | CHClassHook1(FavRecordDetailViewController, viewWillDisappear); 136 | 137 | CHLoadLateClass(MsgRecordDetailViewController); 138 | CHClassHook2(MsgRecordDetailViewController, actionSheet, clickedButtonAtIndex); 139 | CHClassHook2(MsgRecordDetailViewController, tableView, cellForRowAtIndexPath); 140 | CHClassHook1(MsgRecordDetailViewController, viewWillAppear); 141 | CHClassHook1(MsgRecordDetailViewController, viewWillDisappear); 142 | } 143 | 144 | -------------------------------------------------------------------------------- /WeChatTDylib/ReverseDemoDylib.mm: -------------------------------------------------------------------------------- 1 | #line 1 "/Users/wangsuyan/Desktop/baidu/reverse/ReverseDemo/ReverseDemoDylib/ReverseDemoDylib.xm" 2 | 3 | 4 | #import 5 | 6 | 7 | #include 8 | #if defined(__clang__) 9 | #if __has_feature(objc_arc) 10 | #define _LOGOS_SELF_TYPE_NORMAL __unsafe_unretained 11 | #define _LOGOS_SELF_TYPE_INIT __attribute__((ns_consumed)) 12 | #define _LOGOS_SELF_CONST const 13 | #define _LOGOS_RETURN_RETAINED __attribute__((ns_returns_retained)) 14 | #else 15 | #define _LOGOS_SELF_TYPE_NORMAL 16 | #define _LOGOS_SELF_TYPE_INIT 17 | #define _LOGOS_SELF_CONST 18 | #define _LOGOS_RETURN_RETAINED 19 | #endif 20 | #else 21 | #define _LOGOS_SELF_TYPE_NORMAL 22 | #define _LOGOS_SELF_TYPE_INIT 23 | #define _LOGOS_SELF_CONST 24 | #define _LOGOS_RETURN_RETAINED 25 | #endif 26 | 27 | @class ClassName; 28 | static id (*_logos_meta_orig$_ungrouped$ClassName$sharedInstance)(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static id _logos_meta_method$_ungrouped$ClassName$sharedInstance(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$ClassName$messageWithNoReturnAndOneArgument$)(_LOGOS_SELF_TYPE_NORMAL ClassName* _LOGOS_SELF_CONST, SEL, id); static void _logos_method$_ungrouped$ClassName$messageWithNoReturnAndOneArgument$(_LOGOS_SELF_TYPE_NORMAL ClassName* _LOGOS_SELF_CONST, SEL, id); static id (*_logos_orig$_ungrouped$ClassName$messageWithReturnAndNoArguments)(_LOGOS_SELF_TYPE_NORMAL ClassName* _LOGOS_SELF_CONST, SEL); static id _logos_method$_ungrouped$ClassName$messageWithReturnAndNoArguments(_LOGOS_SELF_TYPE_NORMAL ClassName* _LOGOS_SELF_CONST, SEL); 29 | 30 | #line 5 "/Users/wangsuyan/Desktop/baidu/reverse/ReverseDemo/ReverseDemoDylib/ReverseDemoDylib.xm" 31 | 32 | 33 | 34 | static id _logos_meta_method$_ungrouped$ClassName$sharedInstance(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST __unused self, SEL __unused _cmd) { 35 | HBLogDebug(@"+[ sharedInstance]", self); 36 | 37 | return _logos_meta_orig$_ungrouped$ClassName$sharedInstance(self, _cmd); 38 | } 39 | 40 | 41 | static void _logos_method$_ungrouped$ClassName$messageWithNoReturnAndOneArgument$(_LOGOS_SELF_TYPE_NORMAL ClassName* _LOGOS_SELF_CONST __unused self, SEL __unused _cmd, id originalArgument) { 42 | HBLogDebug(@"-[ messageWithNoReturnAndOneArgument:%@]", self, originalArgument); 43 | 44 | _logos_orig$_ungrouped$ClassName$messageWithNoReturnAndOneArgument$(self, _cmd, originalArgument); 45 | 46 | 47 | } 48 | 49 | 50 | static id _logos_method$_ungrouped$ClassName$messageWithReturnAndNoArguments(_LOGOS_SELF_TYPE_NORMAL ClassName* _LOGOS_SELF_CONST __unused self, SEL __unused _cmd) { 51 | HBLogDebug(@"-[ messageWithReturnAndNoArguments]", self); 52 | 53 | id originalReturnOfMessage = _logos_orig$_ungrouped$ClassName$messageWithReturnAndNoArguments(self, _cmd); 54 | 55 | 56 | 57 | return originalReturnOfMessage; 58 | } 59 | 60 | 61 | static __attribute__((constructor)) void _logosLocalInit() { 62 | {Class _logos_class$_ungrouped$ClassName = objc_getClass("ClassName"); Class _logos_metaclass$_ungrouped$ClassName = object_getClass(_logos_class$_ungrouped$ClassName); MSHookMessageEx(_logos_metaclass$_ungrouped$ClassName, @selector(sharedInstance), (IMP)&_logos_meta_method$_ungrouped$ClassName$sharedInstance, (IMP*)&_logos_meta_orig$_ungrouped$ClassName$sharedInstance);MSHookMessageEx(_logos_class$_ungrouped$ClassName, @selector(messageWithNoReturnAndOneArgument:), (IMP)&_logos_method$_ungrouped$ClassName$messageWithNoReturnAndOneArgument$, (IMP*)&_logos_orig$_ungrouped$ClassName$messageWithNoReturnAndOneArgument$);MSHookMessageEx(_logos_class$_ungrouped$ClassName, @selector(messageWithReturnAndNoArguments), (IMP)&_logos_method$_ungrouped$ClassName$messageWithReturnAndNoArguments, (IMP*)&_logos_orig$_ungrouped$ClassName$messageWithReturnAndNoArguments);} } 63 | #line 35 "/Users/wangsuyan/Desktop/baidu/reverse/ReverseDemo/ReverseDemoDylib/ReverseDemoDylib.xm" 64 | -------------------------------------------------------------------------------- /WeChatTDylib/ReverseDemoDylib.xm: -------------------------------------------------------------------------------- 1 | // See http://iphonedevwiki.net/index.php/Logos 2 | 3 | #import 4 | 5 | %hook ClassName 6 | 7 | + (id)sharedInstance 8 | { 9 | %log; 10 | 11 | return %orig; 12 | } 13 | 14 | - (void)messageWithNoReturnAndOneArgument:(id)originalArgument 15 | { 16 | %log; 17 | 18 | %orig(originalArgument); 19 | 20 | // or, for exmaple, you could use a custom value instead of the original argument: %orig(customValue); 21 | } 22 | 23 | - (id)messageWithReturnAndNoArguments 24 | { 25 | %log; 26 | 27 | id originalReturnOfMessage = %orig; 28 | 29 | // for example, you could modify the original return value before returning it: [SomeOtherClass doSomethingToThisObject:originalReturnOfMessage]; 30 | 31 | return originalReturnOfMessage; 32 | } 33 | 34 | %end 35 | -------------------------------------------------------------------------------- /WeChatTDylib/WeChatHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // WeChatHeader.h 3 | // WeChatTDylib 4 | // 5 | // Created by Wang,Suyan on 2018/1/31. 6 | // Copyright © 2018年 Wang,Suyan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | static NSString * const kScreenshotTitle = @"截图"; 13 | static NSString * const kScreenshotTitleMask = @"截图(马赛克)"; 14 | static NSString * const kScreenshotTitlePreview = @"预览"; 15 | static NSString * const kScreenshotLogoTitle = @"by公众号 知识小集"; 16 | 17 | @interface WeChatHeader : NSObject 18 | + (UIImage *)captureScrollView:(UIScrollView *)view; 19 | + (UIImage *)addWatemarkTextWithImage:(UIImage *)image text:(NSString *)text; 20 | @end 21 | 22 | @interface MMHeadImageView : UIView 23 | - (void)updateUsrName:(id)arg1 withHeadImgUrl:(id)arg2; 24 | - (NSString *)getRealUserName:(id)arg1; 25 | @end 26 | 27 | @interface WCActionSheet: UIWindow 28 | @property(strong, nonatomic) NSMutableArray *buttonTitleList; 29 | - (void)showInView:(id)arg1; 30 | - (long long)addButtonWithItem:(id)arg1 atIndex:(unsigned long long)arg2; 31 | - (long long)addButtonWithTitle:(id)arg1 atIndex:(unsigned long long)arg2; 32 | - (long long)addButtonWithTitle:(id)arg1; 33 | @end 34 | 35 | @interface WCActionSheetItem: NSObject 36 | @property(copy, nonatomic) NSString *title; 37 | - (WCActionSheetItem *)initWithTitle:(id)arg1; 38 | - (double)getItemHeight; 39 | @end 40 | 41 | 42 | @interface MMTableView: UITableView 43 | @end 44 | 45 | @interface FavRecordBaseNodeView: UIView 46 | { 47 | UILabel *m_srcTitleLabel; 48 | // MMUILongPressImageView *_headImageView; 49 | UIView *m_headImg; 50 | } 51 | @end 52 | 53 | @interface FavBaseDetailViewController: UIViewController 54 | { 55 | MMTableView *m_tableView; 56 | } 57 | - (UITableViewCell *)tableView:(id)arg1 cellForRowAtIndexPath:(id)arg2; 58 | 59 | @end 60 | 61 | @interface FavRecordDetailViewController: FavBaseDetailViewController 62 | { 63 | WCActionSheet *favImgLongPressAction; 64 | } 65 | - (void)viewWillDisappear:(_Bool)arg1; 66 | - (void)viewWillAppear:(_Bool)arg1; 67 | // WCActionSheetDelegate 68 | - (void)actionSheet:(id)arg1 clickedButtonAtIndex:(long long)arg2; 69 | // 下面这两个协议没有实现,不然直接在这里添加菜单,多好,试着给这个类添加新的方法也不好使 70 | - (void)didPresentActionSheet:(WCActionSheet *)arg1; 71 | - (void)willPresentActionSheet:(WCActionSheet *)arg1; 72 | 73 | @end 74 | 75 | // 群聊记录 76 | @interface MsgRecordDetailViewController: UIViewController 77 | { 78 | MMTableView *m_tableView; 79 | } 80 | - (void)viewWillAppear:(_Bool)arg1; 81 | - (void)viewWillDisappear:(_Bool)arg1; 82 | - (void)actionSheet:(id)arg1 clickedButtonAtIndex:(long long)arg2;; 83 | - (UITableViewCell *)tableView:(id)arg1 cellForRowAtIndexPath:(id)arg2; 84 | @end 85 | 86 | -------------------------------------------------------------------------------- /WeChatTDylib/WeChatHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // WeChatHeader.m 3 | // WeChatTDylib 4 | // 5 | // Created by Wang,Suyan on 2018/1/31. 6 | // Copyright © 2018年 Wang,Suyan. All rights reserved. 7 | // 8 | 9 | #import "WeChatHeader.h" 10 | #import 11 | 12 | static SystemSoundID sysSoundID = 1108; 13 | 14 | 15 | @implementation WeChatHeader 16 | 17 | + (UIImage *)captureScrollView:(UIScrollView *)view 18 | { 19 | if (@available(iOS 9.0, *)) { 20 | AudioServicesPlaySystemSoundWithCompletion(sysSoundID, ^{ 21 | AudioServicesDisposeSystemSoundID(sysSoundID); 22 | }); 23 | } 24 | 25 | CGRect tempFrame = view.frame; 26 | CGPoint tempPoint = view.contentOffset; 27 | 28 | view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.contentSize.width, view.contentSize.height); 29 | 30 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.contentSize.width, view.contentSize.height), NO, [UIScreen mainScreen].scale); 31 | CGContextRef context = UIGraphicsGetCurrentContext(); 32 | [view.layer renderInContext: context]; 33 | UIImage* image = UIGraphicsGetImageFromCurrentImageContext(); 34 | UIGraphicsEndImageContext(); 35 | 36 | view.contentOffset = tempPoint; 37 | view.frame = tempFrame; 38 | 39 | return image; 40 | } 41 | 42 | + (UIImage *)addWatemarkTextWithImage:(UIImage *)image text:(NSString *)text 43 | { 44 | CGFloat w = image.size.width; 45 | CGFloat h = image.size.height; 46 | UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale); 47 | [[UIColor whiteColor] set]; 48 | [image drawInRect:CGRectMake(0, 0, w, h)]; 49 | UIFont * font = [UIFont systemFontOfSize:16]; 50 | CGFloat textWidth = [self widthWithHeight:30 font:font title:text]; 51 | [text drawInRect:CGRectMake(w - textWidth - 20, h - 30, w, 30) withAttributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:[UIColor lightGrayColor]}]; 52 | UIImage * resultImage = UIGraphicsGetImageFromCurrentImageContext(); 53 | UIGraphicsEndImageContext(); 54 | return resultImage; 55 | } 56 | 57 | + (CGRect)rectWithSize:(CGSize)size font:(UIFont *)font title:(NSString *)title 58 | { 59 | CGFloat lineSpace = 3; 60 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 61 | paragraphStyle.lineSpacing = lineSpace; 62 | NSDictionary *attrs = @{ 63 | NSFontAttributeName : font, 64 | NSParagraphStyleAttributeName : paragraphStyle 65 | }; 66 | 67 | return [title boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil]; 68 | } 69 | 70 | + (CGFloat)widthWithHeight:(CGFloat)height font:(UIFont *)font title:(NSString *)title 71 | { 72 | return [self rectWithSize:CGSizeMake(MAXFLOAT, height) font:font title:title].size.width; 73 | } 74 | 75 | @end 76 | 77 | -------------------------------------------------------------------------------- /WeChatTDylib/WeChatSaveData.h: -------------------------------------------------------------------------------- 1 | // 2 | // WeChatSaveData.h 3 | // WeChatTDylib 4 | // 5 | // Created by Wang,Suyan on 2018/3/31. 6 | // Copyright © 2018年 Wang,Suyan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WeChatHeader.h" 11 | 12 | 13 | typedef NS_ENUM(NSUInteger, WeChatSaveDataMaskType) { 14 | WeChatSaveDataMaskTypeNoSet, 15 | WeChatSaveDataMaskTypeNoMask, 16 | WeChatSaveDataMaskTypeMast, 17 | WeChatSaveDataMaskTypePreview, // 预览 18 | }; 19 | 20 | @interface WeChatSaveData : NSObject 21 | 22 | + (instancetype)defaultSaveData; 23 | 24 | @property (nonatomic, assign) WeChatSaveDataMaskType maskType; 25 | @property (nonatomic, strong) NSMutableDictionary *userNameDict; 26 | @property (nonatomic, assign) BOOL isNeedAddMenu; 27 | 28 | @end 29 | 30 | 31 | @interface WeChatCapture : NSObject 32 | 33 | + (void)saveCaptureImageWithSheet:(WCActionSheet *)sheet index:(NSInteger)index viewController:(UIViewController *)viewController; 34 | + (void)updateCellDataWithCell:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath; 35 | + (void)viewWillDisappearAction; 36 | + (void)viewWillAppearAction; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /WeChatTDylib/WeChatSaveData.m: -------------------------------------------------------------------------------- 1 | // 2 | // WeChatSaveData.m 3 | // WeChatTDylib 4 | // 5 | // Created by Wang,Suyan on 2018/3/31. 6 | // Copyright © 2018年 Wang,Suyan. All rights reserved. 7 | // 8 | 9 | #import "WeChatSaveData.h" 10 | #import "WeChatUser.h" 11 | #import "WeChatUser.h" 12 | 13 | @implementation WeChatSaveData 14 | 15 | + (instancetype)defaultSaveData 16 | { 17 | static id manager; 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | manager = [[self alloc] init]; 21 | }); 22 | 23 | return manager; 24 | } 25 | 26 | - (instancetype)init 27 | { 28 | self = [super init]; 29 | if (self) { 30 | _userNameDict = [NSMutableDictionary dictionary]; 31 | } 32 | return self; 33 | } 34 | 35 | @end 36 | 37 | 38 | @implementation WeChatCapture 39 | 40 | + (void)saveCaptureImageWithSheet:(WCActionSheet *)sheet index:(NSInteger)index viewController:(UIViewController *)viewController 41 | { 42 | WCActionSheetItem *item = sheet.buttonTitleList[index]; 43 | if([item.title isEqualToString:kScreenshotTitleMask]){ 44 | [WeChatSaveData defaultSaveData].maskType = WeChatSaveDataMaskTypeMast; 45 | [self saveImageWithViewController:viewController]; 46 | } else if ([item.title isEqualToString:kScreenshotTitle]){ 47 | [WeChatSaveData defaultSaveData].maskType = WeChatSaveDataMaskTypeNoMask; 48 | [self saveImageWithViewController:viewController]; 49 | } else if ([item.title isEqualToString:kScreenshotTitlePreview]){ 50 | [WeChatSaveData defaultSaveData].maskType = WeChatSaveDataMaskTypePreview; 51 | MMTableView *tableView = [viewController valueForKeyPath:@"m_tableView"]; 52 | [tableView reloadData]; 53 | [WeChatUser resetIndex]; 54 | } else { 55 | [WeChatSaveData defaultSaveData].maskType = WeChatSaveDataMaskTypeNoSet; 56 | } 57 | } 58 | 59 | + (void)saveImageWithViewController:(UIViewController *)viewController 60 | { 61 | MMTableView *tableView = [viewController valueForKeyPath:@"m_tableView"]; 62 | [tableView reloadData]; 63 | dispatch_after(2, dispatch_get_main_queue(), ^{ 64 | UIImage *image = [WeChatHeader captureScrollView:tableView]; 65 | if(image) { 66 | UIImage *reImage = [WeChatHeader addWatemarkTextWithImage:image text:kScreenshotLogoTitle]; 67 | UIImageWriteToSavedPhotosAlbum(reImage, nil, nil, 0); 68 | } 69 | [WeChatUser resetIndex]; 70 | }); 71 | } 72 | 73 | + (void)updateCellDataWithCell:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath 74 | { 75 | if ([WeChatSaveData defaultSaveData].maskType == WeChatSaveDataMaskTypeMast || [WeChatSaveData defaultSaveData].maskType == WeChatSaveDataMaskTypePreview) { 76 | NSArray *subviews = [cell.contentView subviews]; 77 | FavRecordBaseNodeView *nodeView = [subviews lastObject]; 78 | if ([NSStringFromClass([nodeView class]) hasSuffix:@"NodeView"]) { 79 | UILabel *nickNameLabel = [nodeView valueForKey:@"m_srcTitleLabel"]; 80 | if (nickNameLabel) { 81 | CGRect tempFrame = nickNameLabel.frame; 82 | tempFrame.size.width = 120; 83 | nickNameLabel.frame = tempFrame; 84 | } 85 | 86 | MMHeadImageView *imageView = [nodeView valueForKeyPath:@"m_headImg"]; 87 | NSString *nickName = [imageView valueForKey:@"_nsUsrName"]; 88 | WeChatUser *aUser = [[WeChatSaveData defaultSaveData].userNameDict objectForKey:nickName?:@""]; 89 | if (!aUser) { 90 | aUser = [WeChatUser user]; 91 | [[WeChatSaveData defaultSaveData].userNameDict setObject:aUser forKey:nickName?:@""]; 92 | } 93 | nickNameLabel.text = aUser.nickname ?: @""; 94 | if (imageView) { 95 | [imageView updateUsrName:aUser.nickname withHeadImgUrl:aUser.icon]; 96 | } 97 | } 98 | } 99 | } 100 | 101 | + (void)viewWillDisappearAction 102 | { 103 | [WeChatSaveData defaultSaveData].isNeedAddMenu = NO; 104 | [WeChatSaveData defaultSaveData].maskType = WeChatSaveDataMaskTypeNoSet; 105 | [[WeChatSaveData defaultSaveData].userNameDict removeAllObjects]; 106 | } 107 | 108 | + (void)viewWillAppearAction 109 | { 110 | [WeChatSaveData defaultSaveData].isNeedAddMenu = YES; 111 | [WeChatSaveData defaultSaveData].maskType = WeChatSaveDataMaskTypeNoSet; 112 | } 113 | 114 | @end 115 | 116 | -------------------------------------------------------------------------------- /WeChatTDylib/WeChatUser.h: -------------------------------------------------------------------------------- 1 | // 2 | // WeChatUser.h 3 | // WeChatTDylib 4 | // 5 | // Created by Wang,Suyan on 2018/4/3. 6 | // Copyright © 2018年 Wang,Suyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WeChatUser : NSObject 12 | 13 | + (WeChatUser *)user; 14 | + (void)resetIndex; 15 | 16 | @property (nonatomic, copy) NSString *icon; 17 | @property (nonatomic, copy) NSString *nickname; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /WeChatTDylib/WeChatUser.m: -------------------------------------------------------------------------------- 1 | // 2 | // WeChatUser.m 3 | // WeChatTDylib 4 | // 5 | // Created by Wang,Suyan on 2018/4/3. 6 | // Copyright © 2018年 Wang,Suyan. All rights reserved. 7 | // 8 | 9 | #import "WeChatUser.h" 10 | 11 | static NSInteger user_index = 0; 12 | 13 | @implementation WeChatUser 14 | 15 | + (WeChatUser *)user 16 | { 17 | WeChatUser *aUser = [[WeChatUser alloc] init]; 18 | if (user_index >= [self userArray].count) { 19 | return aUser; 20 | } 21 | 22 | NSDictionary *userDict = [[self userArray] objectAtIndex:user_index]; 23 | aUser.icon = userDict[@"icon"]; 24 | aUser.nickname = userDict[@"nickname"]; 25 | 26 | user_index += 1; 27 | 28 | return aUser; 29 | } 30 | 31 | + (void)resetIndex 32 | { 33 | user_index = 0; 34 | } 35 | 36 | + (NSArray *)userArray 37 | { 38 | static dispatch_once_t onceToken; 39 | static NSArray *users; 40 | dispatch_once(&onceToken, ^{ 41 | users = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"user" ofType:@"plist"]]; 42 | }); 43 | return users; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /WeChatTDylib/fishhook/fishhook.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #import "fishhook.h" 25 | 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | 34 | #ifdef __LP64__ 35 | typedef struct mach_header_64 mach_header_t; 36 | typedef struct segment_command_64 segment_command_t; 37 | typedef struct section_64 section_t; 38 | typedef struct nlist_64 nlist_t; 39 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 40 | #else 41 | typedef struct mach_header mach_header_t; 42 | typedef struct segment_command segment_command_t; 43 | typedef struct section section_t; 44 | typedef struct nlist nlist_t; 45 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT 46 | #endif 47 | 48 | #ifndef SEG_DATA_CONST 49 | #define SEG_DATA_CONST "__DATA_CONST" 50 | #endif 51 | 52 | struct rebindings_entry { 53 | struct rebinding *rebindings; 54 | size_t rebindings_nel; 55 | struct rebindings_entry *next; 56 | }; 57 | 58 | static struct rebindings_entry *_rebindings_head; 59 | 60 | static int prepend_rebindings(struct rebindings_entry **rebindings_head, 61 | struct rebinding rebindings[], 62 | size_t nel) { 63 | struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); 64 | if (!new_entry) { 65 | return -1; 66 | } 67 | new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel); 68 | if (!new_entry->rebindings) { 69 | free(new_entry); 70 | return -1; 71 | } 72 | memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); 73 | new_entry->rebindings_nel = nel; 74 | new_entry->next = *rebindings_head; 75 | *rebindings_head = new_entry; 76 | return 0; 77 | } 78 | 79 | static void perform_rebinding_with_section(struct rebindings_entry *rebindings, 80 | section_t *section, 81 | intptr_t slide, 82 | nlist_t *symtab, 83 | char *strtab, 84 | uint32_t *indirect_symtab) { 85 | uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; 86 | void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); 87 | for (uint i = 0; i < section->size / sizeof(void *); i++) { 88 | uint32_t symtab_index = indirect_symbol_indices[i]; 89 | if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || 90 | symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { 91 | continue; 92 | } 93 | uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; 94 | char *symbol_name = strtab + strtab_offset; 95 | if (strnlen(symbol_name, 2) < 2) { 96 | continue; 97 | } 98 | struct rebindings_entry *cur = rebindings; 99 | while (cur) { 100 | for (uint j = 0; j < cur->rebindings_nel; j++) { 101 | if (strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { 102 | if (cur->rebindings[j].replaced != NULL && 103 | indirect_symbol_bindings[i] != cur->rebindings[j].replacement) { 104 | *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; 105 | } 106 | indirect_symbol_bindings[i] = cur->rebindings[j].replacement; 107 | goto symbol_loop; 108 | } 109 | } 110 | cur = cur->next; 111 | } 112 | symbol_loop:; 113 | } 114 | } 115 | 116 | static void rebind_symbols_for_image(struct rebindings_entry *rebindings, 117 | const struct mach_header *header, 118 | intptr_t slide) { 119 | Dl_info info; 120 | if (dladdr(header, &info) == 0) { 121 | return; 122 | } 123 | 124 | segment_command_t *cur_seg_cmd; 125 | segment_command_t *linkedit_segment = NULL; 126 | struct symtab_command* symtab_cmd = NULL; 127 | struct dysymtab_command* dysymtab_cmd = NULL; 128 | 129 | uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); 130 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 131 | cur_seg_cmd = (segment_command_t *)cur; 132 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 133 | if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { 134 | linkedit_segment = cur_seg_cmd; 135 | } 136 | } else if (cur_seg_cmd->cmd == LC_SYMTAB) { 137 | symtab_cmd = (struct symtab_command*)cur_seg_cmd; 138 | } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { 139 | dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; 140 | } 141 | } 142 | 143 | if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || 144 | !dysymtab_cmd->nindirectsyms) { 145 | return; 146 | } 147 | 148 | // Find base symbol/string table addresses 149 | uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; 150 | nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); 151 | char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); 152 | 153 | // Get indirect symbol table (array of uint32_t indices into symbol table) 154 | uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); 155 | 156 | cur = (uintptr_t)header + sizeof(mach_header_t); 157 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 158 | cur_seg_cmd = (segment_command_t *)cur; 159 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 160 | if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && 161 | strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { 162 | continue; 163 | } 164 | for (uint j = 0; j < cur_seg_cmd->nsects; j++) { 165 | section_t *sect = 166 | (section_t *)(cur + sizeof(segment_command_t)) + j; 167 | if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { 168 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 169 | } 170 | if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { 171 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 172 | } 173 | } 174 | } 175 | } 176 | } 177 | 178 | static void _rebind_symbols_for_image(const struct mach_header *header, 179 | intptr_t slide) { 180 | rebind_symbols_for_image(_rebindings_head, header, slide); 181 | } 182 | 183 | int rebind_symbols_image(void *header, 184 | intptr_t slide, 185 | struct rebinding rebindings[], 186 | size_t rebindings_nel) { 187 | struct rebindings_entry *rebindings_head = NULL; 188 | int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); 189 | rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); 190 | free(rebindings_head); 191 | return retval; 192 | } 193 | 194 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { 195 | int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); 196 | if (retval < 0) { 197 | return retval; 198 | } 199 | // If this was the first call, register callback for image additions (which is also invoked for 200 | // existing images, otherwise, just run on existing images 201 | if (!_rebindings_head->next) { 202 | _dyld_register_func_for_add_image(_rebind_symbols_for_image); 203 | } else { 204 | uint32_t c = _dyld_image_count(); 205 | for (uint32_t i = 0; i < c; i++) { 206 | _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); 207 | } 208 | } 209 | return retval; 210 | } 211 | -------------------------------------------------------------------------------- /WeChatTDylib/fishhook/fishhook.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #ifndef fishhook_h 25 | #define fishhook_h 26 | 27 | #include 28 | #include 29 | 30 | #if !defined(FISHHOOK_EXPORT) 31 | #define FISHHOOK_VISIBILITY __attribute__((visibility("hidden"))) 32 | #else 33 | #define FISHHOOK_VISIBILITY __attribute__((visibility("default"))) 34 | #endif 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif //__cplusplus 39 | 40 | /* 41 | * A structure representing a particular intended rebinding from a symbol 42 | * name to its replacement 43 | */ 44 | struct rebinding { 45 | const char *name; 46 | void *replacement; 47 | void **replaced; 48 | }; 49 | 50 | /* 51 | * For each rebinding in rebindings, rebinds references to external, indirect 52 | * symbols with the specified name to instead point at replacement for each 53 | * image in the calling process as well as for all future images that are loaded 54 | * by the process. If rebind_functions is called more than once, the symbols to 55 | * rebind are added to the existing list of rebindings, and if a given symbol 56 | * is rebound more than once, the later rebinding will take precedence. 57 | */ 58 | FISHHOOK_VISIBILITY 59 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); 60 | 61 | /* 62 | * Rebinds as above, but only in the specified image. The header should point 63 | * to the mach-o header, the slide should be the slide offset. Others as above. 64 | */ 65 | FISHHOOK_VISIBILITY 66 | int rebind_symbols_image(void *header, 67 | intptr_t slide, 68 | struct rebinding rebindings[], 69 | size_t rebindings_nel); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif //__cplusplus 74 | 75 | #endif //fishhook_h 76 | 77 | -------------------------------------------------------------------------------- /WeChatTDylib/teachset.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lefex/WeChatShot/e80606f5bc51c17ec240ded4547cbfb9ade4bce1/WeChatTDylib/teachset.jpg -------------------------------------------------------------------------------- /images/pic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lefex/WeChatShot/e80606f5bc51c17ec240ded4547cbfb9ade4bce1/images/pic1.png -------------------------------------------------------------------------------- /images/pic2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lefex/WeChatShot/e80606f5bc51c17ec240ded4547cbfb9ade4bce1/images/pic2.jpeg -------------------------------------------------------------------------------- /images/xiaoguo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lefex/WeChatShot/e80606f5bc51c17ec240ded4547cbfb9ade4bce1/images/xiaoguo.gif -------------------------------------------------------------------------------- /podlib/source/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /podlib/source/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /podlib/source/.idea/source.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /podlib/source/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 80 | 81 | 82 | 83 | json 84 | POD_DB_NAME 85 | query_update_star 86 | parse_podreps 87 | pod_update_podlib2 88 | POD_NOT_PODNAME 89 | list 90 | 91 | 92 | 93 | 107 | 108 | 109 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 |