preface
Article 10 – application signature, we use two ways CodeSign terminal instructions and Shell script, the successful implementation of the heavy signature WeChat app, can already see WeChat login UI levels of registration page, and then, we want to do your own thing, such as injection of your own code, modify the WeChat register or login process.
Code injection
Generally, the original program is modified by code injection. The injected code will choose to use the Framework or third-party libraries such as Dylib to inject.
1.1 FrameWork into
We know that after re-signing app, the shell project code will be replaced (replaced by MachO level 2 executable file), so the original project code will not be executed. IOS system loads the MachO executable through dyLD (dynamic linker provided by iOS system), and the loading process begins with the Load Commands of MachO (including _PAGEZERO, _TEXT, _DATA, _LINKEDIT, etc.) π
In addition to executing its own engineering code, the App also relies on some system base libraries (UIKit, Foudation, etc.) and third-party libraries (.framwork,.A library, etc.). These libraries are ultimately a MachO executable. Analyzing the MachO file, you will find that the Frameworks library exists in Load Commands as LC_LOAD_DYLIB, and the path to the corresponding Frameworks library is π
As long as the Load Command has the library LC_LOAD_DYLIB, dyld will Load the library in the corresponding path.
With this in mind, the process of code injection is clear π
- Confirm the target App
MachO
Executable fileLoad Commands
There is a corresponding inLC_LOAD_DYLIB
- Write your own code to the dynamic library
1.1.1 Injection Procedure
- Add one to your shell project
target
πLGHook Framework
The dynamic library
β οΈ Note: The shell project here uses the shell script re-signing pattern from the previous article 10- Applying re-signing.
LGHook
addLGInject
Class, and override+load
methods
If LGHook is loaded into memory, log is printed.
run
To see theProduces
In the.app
The dynamic library LGHook is already in the Framewroks of the target App, then we look at MachOπ
- Look at the MachO file
LGHook LC_LOAD_DYLIB does not exist in Load Commands, so run does not print logs in the console.
1.1.2 Yololib manual injection
At this point, you need to modify the MachO file using yololib tool to actually add LGHook to the Load Commands of the target App π
- copy
yololib
And the target AppExecutable file
toThe same directory
π (A separate new
A folder)
Then run π
./yololib The name of the Framework path to add to the target executable
For example π
./yololib WeChat Frameworks/LGHook.framework/LGHook
Copy the code
Look again at the executable file Load Commandsπ
This is where LGHook comes in.
- take
Original. Ipa package
, decompress, inPayload
Found in the folder.app
Right,Display package contents
To replaceThe previous step
The generatedBinary MachO file
, back toPayload
In the directory, enter the following command to generate.ipa
package
zip -ry xx.ipa Payload/
Copy the code
β οΈ Note: This step must replace the binary MachO files in the original.ipa package.
- The original package
WeChat 8.0.2. Ipa
, change the suffix to.zip
, decompression π
- Right click
Display package contents
π
- Replace the one from the previous step
WeChat
Mach-O
π
- Return to the
Payload
In the directory, enter the commandGenerated. Ipa package
π
- Put the. Ipa package generated in the previous step into the APP folder π
CMD +shift+k clear the XCode project cache, then runπ
Solve π
Modify the lowest version supported by lghook. framework.
The run again π
And you’re done! πΊ πΊ πΊ πΊ πΊ πΊ πΊ πΊ πΊ
summary
The above steps are roughly π
- New shell project
WeChat
, the configurationRe-signing script
- Create a new one with Xcode
LGHook.framwork
(noteChange the supported version
), run the real machine, install the library into the APP package - through
yololib
forWeChat
theMachO
injectionLGHook.framwork
The library path. Command π$./yololib MachO file path Library path
- Use the original wechat. Ipa package that will be generated in step 3
MachO
, replace the original package insideMachO
, again packaged to generate.ipa
package - Shell project
The APP folder
Replace the one generated in step 4The ipa package
, run directly.
All Framwork loading is performed by DYLD loading into memory. The successfully injected library path is written to the LC_LOAD_DYLIB of the MachO file.
1.2 Dylib injection
In addition to Framwork injection, Dylib injection is also available. The principle is the same as Framwork, the process is similar to Framwork.
- Create the dylib library. So I chose
macOS
, for the sake of libraryThe dynamic library
π
Modify theBase SDK
foriOS
π
The Code Signing Identity is changed to iOS Developerπ
Add Copy Files to build Phases and add libLGDylibHook. Dylib π
- LGDylibHook. M add Hook code π
+ (void) load {NSLog (@ "LGDylibHook Success πΊ πΊ πΊ πΊ πΊ πΊ πΊ πΊ"); }Copy the code
Run, at this point, is still justFrameworks
In theHave libLGDylibHook dylib
Library,MachO
In theLoad Commands
stillThere is no LC_LOAD_DYLIB
.
1.2.1 Yololib automatic injection
- Directly in the
appResign.sh
Add to the scriptyololib
theModify the MachO
Script instructions π
# modified yololib MachO file #. / yololib "$TARGET_APP_PATH / $APP_BINARY" "Frameworks/LGHook. Framework/LGHook". / yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libLGDylibHook.dylib"Copy the code
Place the Yololib library in the project directory π
- To observe the
libLGDylibHook.dylib
Whether inFrameworks
In π
The Load in the MachO Commands π
- run
β οΈ Note: you need to compile the library libLGDylibHook. Dylib first, and then compile the shell project
summary
Injection steps
- Create a new one with Xcode
dylib
Libraries (note: Dylib belongs toMacOS
, so needModify the properties
)Base SDK
foriOS
Code Signing Identity
Modified toiOS Developer
- add
The Target depends on
Will, let XcodeCustom Dylib
filePackage into APP
package - Yololib was used for injection
Second, the example demonstration
Next, we practice, for wechat registration and login page, code HOOK registration and login process. First of all, register. We look at the UI level of the register button π
Above, print the addresses, the Target is WCAccountLoginControlLogic found the sign up button, the action is onFirstViewRegister.
We then hook this method directly to intercept the registration event π
#import "LGDylibHook.h" #import <objc/runtime.h> @implementation LGDylibHook + (void)load { NSLog(@"LGDylib Hook success"); / / intercept WeChat register event Method oldMethod = class_getInstanceMethod (objc_getClass (" WCAccountLoginControlLogic "), @selector(onFirstViewRegister)); Method newMethod = class_getInstanceMethod(self, @selector(hook_onFirstViewRegister)); method_exchangeImplementations(oldMethod, newMethod); } - (void)hook_onFirstViewRegister { NSLog(@"WeChat click login"); } @endCopy the code
Run it π
Successful intercept! Next, we want to intercept the event, execute our own code, and then restore the original flow. How do we do that? I’m sure you can all think of π Method Swizzle.
Method Swizzle
In OC, the relationship between SEL and IMP is like the table of contents of a book. SEL is the method number, just like the title. IMP is the real address of the method implementation, just like the page number. There is a one-to-one correspondence. The Runtime provides a function to swap SEL and IMP mappings.
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
Copy the code
The technique of swapping SEL and IMP mappings using this function is called Method Swizzle.
2.2 Login Debugging Analysis
Next, let’s debug the [login] function π
Above knowable, view the debug analysis can get Target is WCAccountMainLoginViewController, the action is onNext. Similarly, [login] events can be intercepted, so how to get PWD?
View debug can see that the PWD control is a WCUITextField and see the corresponding text (password). How do we get the password without view Debug?
2.2.1 Dynamic analysis
The first approach is dynamic analysis π, through which control hierarchies and response relationships can be analyzed step by step through response chains π
Combined with Presponder and Pviews analysis. But this way is generally more troublesome.
2.2.2 Static Analysis
π use class-dump to export headers (β οΈswift files do not work).
./class-dump -H WeChat -o ./Headers
Copy the code
After execution, all header files are in the Headers folder π
Too many files 22335, not recommended to open Xcode.
- search
WCAccountMainLoginViewController
To findonNext
π
The onNext method takes no arguments and returns no values. See if there are any other passwords associated with π
We noticed that _textFieldUserPwdItem is the password field. 2. Search WCAccountTextFieldItem
WCBaseTextFieldItem, WCBaseTextFieldItem, WCBaseTextFieldItem
Found the m_textField member variable of type WCUITextField. This is not the input account password control.
- Debug verification
Next let’s verify, is it a password control? π KVC console print view π
Related instructions π
(lldb) po [(WCAccountMainLoginViewController *)0x117813000 valueForKey:@"_textFieldUserPwdItem"]
<WCAccountTextFieldItem: 0x2830f9ec0>
(lldb) po [(WCAccountTextFieldItem *)0x2830f9ec0 valueForKey:@"m_textField"]
<WCUITextField: 0x1120a4400; baseClass = UITextField; frame = (20 0; 345 44); text = 'qwer1234'; opaque = NO; autoresize = W+H; tintColor = UIExtendedSRGBColorSpace 0.027451 0.756863 0.376471 1; gestureRecognizers = <NSArray: 0x2818beca0>; layer = <CALayer: 0x281441dc0>>
(lldb) po [(WCUITextField *)0x1120a4400 text]
qwer1234
Copy the code
Sure enough, I found the corresponding class and the desired password. πΊ πΊ πΊ πΊ πΊ πΊ πΊ πΊ
2.3 Login code injection
β οΈ Note: do not use your common account to try, may be blocked.
Finally, and importantly, inject your own code π
+ (void)load { NSLog(@"LGDylib Hook success"); / / intercept WeChat login event Method oldMethod = class_getInstanceMethod (objc_getClass (" WCAccountMainLoginViewController "), @selector(onNext)); Method newMethod = class_getInstanceMethod(self, @selector(hook_onNext)); method_exchangeImplementations(oldMethod, newMethod); } - (void)hook_onNext { NSLog(@"WeChat click login"); UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSString *pwd = [textField text]; NSLog(@"password: %@",pwd); }Copy the code
Next, in the process we should call back the original method and let the login proceed π
- (void)hook_onNext { NSLog(@"WeChat click login"); UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSString *pwd = [textField text]; NSLog(@"password: %@",pwd); [self hook_onNext]; }Copy the code
Run π
Error: Unable to find method! Are not present in the WCAccountMainLoginViewController hook_onNext method. Because π
Normally we do method swaps in categories, not in categories.
Is there a solution? Of course, there are three kinds π
Class_addMethod way
Use AddMethod method, so that the original method can be called, not because can’t find SEL and crash π
/ / 1. Class_addMethod way + (void) load {/ login/intercept WeChat event Class CLS = objc_getClass (" WCAccountMainLoginViewController "); Method onNext = class_getInstanceMethod(cls, @selector(onNext)); // add a new method to WC class_addMethod(CLS, @selector(new_onNext), new_onNext, "v@:"); // Swap method_exchangeImplementations(onNext, class_getInstanceMethod(CLS, @selector(new_onNext))); Void new_onNext(id self, SEL _cmd) {// Get password UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSString *pwd = [textField text]; NSLog(@"password: %@",pwd); [self performSelector:@selector(new_onNext)]; }Copy the code
Class_replaceMethod way
Replace IMPπ directly with the original method, using class_replaceMethod
/ / 2. Class_replaceMethod way + (void) load {/ login/intercept WeChat event Class CLS = objc_getClass (" WCAccountMainLoginViewController "); Origin_onNext = class_replaceMethod(CLS, @selector(onNext), new_onNext, "v@:"); } // original imp imp (*origin_onNext)(id self, SEL _cmd); Void new_onNext(id self, SEL _cmd) {// Get password UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSString *pwd = [textField text]; NSLog(@"password: %@",pwd); origin_onNext(self,_cmd); }Copy the code
Method_setImplementation way
Using method_setImplementation, reassign the original IMPπ directly
/ / 3. Method_setImplementation way + (void) load {/ login/intercept WeChat event Class CLS = objc_getClass (" WCAccountMainLoginViewController "); OnNext = class_getInstanceMethod(CLS,@selector(onNext)); //get origin_onNext = method_getImplementation(onNext); //set method_setImplementation(onNext, new_onNext); } // original imp imp (*origin_onNext)(id self, SEL _cmd); Void new_onNext(id self, SEL _cmd) {// Get password UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSString *pwd = [textField text]; NSLog(@"password: %@",pwd); origin_onNext(self,_cmd); }Copy the code
The previous three methods can successfully return to the original login process. You can check for yourself.
Demo(including wechat. Ipa original package)
XFResignHook
β οΈ Note: wechat IPA package is too large to upload gitHub, if necessary, please leave a message to send email or private message to me.
conclusion
- Code injection: Framework (recommended)/dylib
- create
Framework/dylib
yololib
Modify theMacho Load Commands
./yololib Executable file to modify Framework/dylib path & name to add
- create
- Debug analysis
dynamic
Debug:view debug
debuggingThe control level
In combination withpresponder
andpviews
static
Analysis: byclass-dump
exportThe header file
Analyzing code logic
- Code Hook
+ load
Method corresponding method of hook corresponding classhook
Method is called back to the original methodclass_addMethod
ππ» allows the original method to be called without the risk ofFailed to find SEL and crashed
class_replaceMethod
ππ» gives the original methodReplace the IMP
method_setImplementation
π π»To assign a value
The originalIMP
A link to the
Github AloneMonkey/MonkeyDev