Why is jailbreak tweak not loaded with App? - ios

I've created a (stripped-down version of my) tweak, which logs all URLs an app instantiates with one specific method. It works fine for several apps, but at the start of one app, the tweak is not loaded. I tried to use other filters, neither the bundle id, the class name nor the executable name worked.
Any idea?
Tweak.xm:
%hook NSURL
+ (instancetype)URLWithString:(NSString *)URLString {
%log;
return %orig;
}
%end
.plist:
{ Filter = { Bundles = ( "com.htsu.hsbcpersonalbanking" ); Executables = ("HSBC"); Classes = ("NSURL"); }; }

Three possibilities:
The binary has __RESTRICTED section and normal injection won't work
otool -l /PATH/TO/BINARY|grep sectname, this would be the case if you see __RESTRICTED in the result. optool will force the injection for you but then again you might need to bypass the app's anti-injection checks as well
Your tweak is not compiled correctly.
See syslog, MobileSubstrate will warn you in syslog if this is the case
That method is not called
try:
%ctor{
NSLog(#"I'm injected");
}
and see if that is logged to rule out possibilities of 1&2

Related

UIDocumentPickerExtensionViewController validTypes is always nil

Currently I'm creating a 'Document Provider Extension' for my iOS app. I'm using the extension from inside my app, as well as from other apps to get access to some app internal files.
When using it internally, I'm creating a 'UIDocumentPickerViewController' for example with the following code:
UIDocumentPickerViewController* documentPicker =
[[UIDocumentPickerViewController alloc]
initWithDocumentTypes:#[#"public.image"]
inMode:UIDocumentPickerModeImport];
The extension runs fine, with one exception:
The 'validTypes' array is always 'nil', so I'm not able to limit the access to valid files only (in the example: image files).
I've also tested the extension with other apps (like Mail or Pixelmator): Same result; runs fine, but validTypes is always nil. Of course I do not know, how Mail or Pixelmator are initializing their document pickers.
Does anyone see my error? Or has anyone seen the same problem? And found a solution??
Many thanks!
Maybe you're just checking the 'validTypes' array to early in the initialization process; I did so some time ago...
When walking through the initialization process of an UIDocumentPickerExtensionViewController step by step, you will see the following behavior:
- (void)viewDidLoad
self.validTypes not set (==nil)
- (void)prepareForPresentationMode:(UIDocumentPickerMode)pMode
self.validTypes is set (!= nil; maybe: #[#"com.adobe.pdf"])
- (void)viewWillLoad:(BOOL)animated
self.validTypes is set (!= nil; maybe: #[#"com.adobe.pdf"])
- (void)viewDidLoad:
self.validTypes is set (!= nil; maybe: #[#"com.adobe.pdf"])
Just try to check for valid types later :-)

Detect if Cycript/Substrate or gdb is attached to an iOS app's process?

I am building an iOS app that transmits sensitive data to my server, and I'm signing my API requests as an additional measure. I want to make reverse engineering as hard as possible, and having used Cycript to find signing keys of some real-world apps, I know it's not hard to find these keys by attaching to a process. I am absolutely aware that if someone is really skilled and tries hard enough, they eventually will exploit, but I'm trying to make it as hard as possible, while still being convenient for myself and users.
I can check for jailbroken status and take additional measures, or I can do SSL pinning, but both are still easy to bypass by attaching to the process and modifying the memory.
Is there any way to detect if something (whether it be Cycript, gdb, or any similar tool that can be used for cracking the process) is attached to the process, while not being rejected from App Store?
EDIT: This is not a duplicate of Detecting if iOS app is run in debugger. That question is more related to outputting and it checks an output stream to identify if there's an output stream attached to a logger, while my question is not related to that (and that check doesn't cover my condition).
gdb detection is doable via the linked stackoverflow question - it uses the kstat to determine if the process is being debugged. This will detect if a debugger is currently attached to the process.
There is also a piece of code - Using the Macro SEC_IS_BEING_DEBUGGED_RETURN_NIL in iOS app - which allows you to throw in a macro that performs the debugger attached check in a variety of locations in your code (it's C/Objective-C).
As for detecting Cycript, when it is run against a process, it injects a dylib into the process to deal with communications between the cycript command line and the process - the library has part of the name looking like cynject. That name doesn't look similar to any libraries that are present on a typical iOS app. This should be detectable with a little loop like (C):
BOOL hasCynject() {
int max = _dyld_image_count();
for (int i = 0; i < max; i++) {
const char *name = _dyld_get_image_name(i);
if (name != NULL) {
if (strstr(name, "cynject") == 0) return YES;
}
}
}
Again, giving it a better name than this would be advisable, as well as obfuscating the string that you're testing.
These are only approaches that can be taken - unfortunately these would only protect you in some ways at run-time, if someone chooses to point IDA or some other disassembler at it then you would not be protected.
The reason that the check for debugger is implemented as a macro is that you would be placing the code in a variety of places in the code, and as a result someone trying to fix it would have to patch the app in a variety of places.
Based on #petesh's answer, I found the below code achieved what I wanted on a jailbroken phone with Cycript. The existence of printf strings is gold to a reverse engineer, so this code is only suitable for demo / crack-me apps.
#include <stdio.h>
#include <string.h>
#include <mach-o/dyld.h>
int main ()
{
int max = _dyld_image_count();
for (int i = 0; i < max; i++) {
const char *name = _dyld_get_image_name(i);
const char needle[11] = "libcycript";
char *ret;
if ((ret = strstr(name, needle)) != NULL){
printf("%s\nThe substring is: %s\n", name, ret);
}
}
return 0;
}
As far as I know, Cycript process injection is made possible by debug symbols. So, if you strip out debug symbols for the App Store release (the default build setting for the Release configuration), that would help.
Another action you could take, which would have no impact on the usability of the App, would be to use an obfuscator. However, this would render any crash reports useless, since you wouldn't be able to make sense of the symbols, even if the crash report was symbolicated.

How to get bundleId of the source app when using share extension in IOS8

I wanna know which app calls the share extension to share image to my app(its bundleId or appName). But I found no way to do it. There is none information about the source app in NSExtensionContext. I would greatly appreciate any help.
To achieve what you're asking:
#ifdef HIDE_POST_DIALOG
- ( void ) willMoveToParentViewController: ( UIViewController * ) parent
{
// This is called at the point where the Post dialog is about to be shown.
NSString * hostBundleID = [parent valueForKey:(#"_hostBundleID")];
NSLog( #"**** parent bundle id is %#" ,hostBundleID);
}
#endif
You don't get to find out what app is hosting your extension. That's not part of the extension system. You get to find out what kind of data the app is passing you and, with action extensions, you get to return some data. But you don't get to find out who you're dealing with, only what data is going back and forth.
Your bundle ID is usually com.yourcompany/yourname.yourappname
If you don't know what Xcode has for your company name, create a new project and the screen that prompts you for your app name will have your company name.
Also you can do this,
In Xcode, select your project, then your target (you should have only one) and then the 'Info' tab. You should be able to see the bundle identifier there.
The answer to this question can be found here:
How to get bundle ID?

Creating a custom iOS Jailbreak keyboard

I have developed a custom input method and now would like to develop a tweak that would register it as a keyboard in iOS.
There are many different keyboards in Cydia (mainly from Chinese developers) such as TouchPal and Baidu Input that appear in settings as a keyboard, so it is definitely possible.
I have tried looking into the following options (barely 4 days in IDA, Xcode with theos and console):
Text Input bundles located in /System/Library/TextInput — seems to have nothing to deal with the keyboards themselves? Some superclass headers are missing (i.e. TIZephyr... classes) so I couldn't quite figure it out. However a native integration would be awesome.
TextInput private framework — also seems to be just for dictionary and so on
UIKit's UIKB.. and UIKeyboard.. classes — UIKeyboardImpl seems to be something related with the keyboard functioning and UIKeyboardLayout is the thing you build upon.
I tried hooking UIKeyboardDictationLayout to just give a plain instance of a UIKeyboardLayout upon initialization — and when I tapped the mic button on the keyboard, the keyboard went blank! That kind of implementation would be nice too (even though killing dictation functionality is undesired). However, I can't find where do I send typing events as well.
So the points are:
What is responsible for registering a class as an input method?
What is responsible for receiving typing events?
I am asking this in hope that there are developers who had to do something similar already, because I couldn't find any articles nor anything that would give me a hint in the header files and bundles.
Thanks in advance.
I got it right this february even though didn't have the time to respond and it's not quite necessary now that iOS 8 has come.
Still, this is how you load your own keyboard:
%hook UIKeyboardInputMode
+ (id)keyboardInputModeWithIdentifier:(id)arg1 {
id o = %orig;
return o;
}
- (id)primaryLanguage {
if([TegakiLayout isTegaki:[self identifier]]) return #"Tegaki";
return %orig;
}
%end
%hook UIKeyboardImpl
/* This is where the magic is! */
+ (Class)layoutClassForInputMode:(NSString*)arg1 keyboardType:(int)arg2 {
Class sass = %orig;
if ([TegakiLayout isTegaki: arg1]) {
return [TegakiLayout class];
}
return sass;
}
%end
extern "C" NSArray*UIKeyboardGetSupportedInputModes();
extern "C" NSArray*UIKeyboardGetActiveInputModes();
static NSArray* (*orig_modes)();
NSArray* rep_modes() {
NSArray* res = [orig_modes() arrayByAddingObjectsFromArray:#[#"TEGAKI", #"TEGAKI_Graffiti"]];
return res;
}
static NSArray* (*orig_active_modes)();
NSArray* rep_active_modes() {
NSArray* res = orig_active_modes();
return res;
}
%ctor {
%init;
MSHookFunction(UIKeyboardGetSupportedInputModes, rep_modes, &orig_modes);
MSHookFunction(UIKeyboardGetActiveInputModes, rep_active_modes, &orig_active_modes);
}
where TegakiLayout is a subclass of UIKeyboardLayout.
You then implement - (BOOL)isAlphabeticPlane for returning whether it's a traditional keyboard thing and do the custom view creation in showKeyboardWithInputTraits:screenTraits:splitTraits:.
To type in you then use [[UIKeyboardImpl activeInstance]insertText:#"\n"];.
To create a 'globe' button you use this:
Class sw = NSClassFromString(#"UIInputSwitcherView");
[[sw sharedInstance]selectNextInputMode];
Don't forget to implement -keyboardName and -keyplaneName as well!
I'll post the whole project one day probably, but for now it's too large to describe here. This should be enough to get you up and running, though.

Why does my iOS app only detect the current language properly on first run?

I am localizing my iOS app, and in the Simulator it runs correctly in my chosen language every time.
When testing on my iPhone 5, it only detects the language properly the first time the app runs. Every other time I recompile and run my app on the device, it detects "en" as the language, even though I am testing with Español ("es") selected.
I detect the language using:
[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]
I've also used:
[[NSLocale preferredLanguages] objectAtIndex:0]
Same result.
If I kill the app after the first run, and restart it on the device, it continues to detect the language properly.
But if I kill the app and then recompile/restart via Xcode after the initial run, it will load with "en" (English) detected instead.
After that, killing and re-starting the app continuously detects as English unless I delete the app completely, and recompile/reinstall/run the app via Xcode. The cycle then repeats... subsequent rebuild/restart without first deleting the app from the device results in misdetection.
All other apps on my device display with Spanish language the entire time. The entire UI shows in Spanish.
UPDATE: I've now tested on my iPad (3rd gen) also running iOS 6, and am experiencing the same behavior.
UPDATE 2:
In didFinishLaunchingWithOptions, I have this code to detect language: (language is an NSString*):
language = [[NSLocale preferredLanguages] objectAtIndex:0];
Followed by this debugging statement, to compare the value I'm getting, as well as a slightly different way of detecting it, just for debugging:
NSLog(#"Detected language: %# / %#", language, [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]);
The output shows as "Detected language: es / es" when the app works properly in Spanish mode, and then shows as "Detected language: en / en" when it doesn't. Still no idea why it decides to load as English sometimes...
UPDATE 4: I appreciate everybody's answers, and I've tried the various suggestions. Unfortunately I was unable to award the +100 bounty as none of the suggestions seemed to fix the issue. If someone does ultimate find a solution that works for me, I will award another +50 bounty to them at that time.
UPDATE 5: I have updated from Xcode 4.5 to 4.5.2, and experiencing this same issue.
UPDATE 6: I have now created a new test project from scratch, and it works perfectly fine! Obviously something must be wrong in the way my project is laid out, or perhaps in one of the data files. I guess my next journey will be to re-create the project from scratch, copying file data over one by one...
UPDATE 7 (MONTHS LATER): Sadly, I am again facing this issue after temporarily resolving it (seemingly) by painstakingly recreating my project. On first load, the language is correctly rendered, but on subsequent loads, it reverts back to English.
SOLVED See my final solution below. Thanks for the help everyone. I may dole out some of the bounty since it will go to waste anyway.
I have FINALLY solved this problem after many months! Thanks to all for the help (I also had some good back and forth with an Apple developer via the dev channels).
TL;DR: I was accidentally syncing language preferences (among many other unexpected things) between devices, using my app's iCloud key value store (via MKiCloudSync)! Read on...
I am using a third-party class called MKiCloudSync, which helps with syncing [NSUserDefaults standardUserDefaults] to my app's iCloud key value store. My intention when I began using it was to let it handle some user favorites syncing in the background.
However, not understanding how standardUserDefaults works, what I didn't realize is that there are a lot of other things being written into standardUserDefaults other than just my own custom app settings!
So what was happening was this:
Start the app up for the first time. Fresh standardUserDefaults in place, and the internal "AppleLanguages" key that stores the ordered list of language preferences is correct based on the current device choices.
App displays properly in the designated language.
In the background, MKiCloudSync syncs ALL standardUserDefaults to iCloud. Conversely, if you had run this app elsewhere, say with an English set device, that device would have also synced it's language settings up to iCloud. So now this current running app is actually having it's language preferences overwritten.
BOOM ... next time the app is run, no matter what you have selected on the device, it's whatever was pulled down from iCloud that will be used as the default language!
What I plan to do to solve the issue with my next app update:
Use a forked version of MKiCloudSync that allows for syncing only whitelisted key names.
Add code that will do a one-time cleanup, first cleaning out the iCloud keystore for my app, then (based on this SO answer), calling this code to reset the user defaults:
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
In my testing so far, this sort of solves the issue... unfortunately, the user would have to restart the app for the language fix to kick in. However, my guess is most users are not experiencing this issue, as they are unlikely to be using multiple devices with different default languages.
In settings->general->international, there is an option to set the location language etc. if you set that to the language you are trying to test it will work if your code is correct.
I tested your steps on my iPhone 5 without issues. This leads me to think there's something else at play here: most probably there's something interferring with the way in which you're reading the locale value.
The steps I'd recommend you take to help you debug this issue are:
Post the complete code of the method in which you're obtaining the preferred language value. Make sure the method is executed each time the app is run.
Make sure the code you post includes the location of the NSLog directive you're using to test for the language setting.
Are you storing the preferred language somewhere else after the first run?
Try with following code:
LocalizationSystem.h===
#import <Foundation/Foundation.h>
#define AMLocalizedString(key, comment) \
[[LocalizationSystem sharedLocalSystem] localizedStringForKey:(key) value:(comment)]
#define LocalizationSetLanguage(language) \
[[LocalizationSystem sharedLocalSystem] setLanguage:(language)]
#define LocalizationGetLanguage \
[[LocalizationSystem sharedLocalSystem] getLanguage]
#define LocalizationReset \
[[LocalizationSystem sharedLocalSystem] resetLocalization]
#interface LocalizationSystem : NSObject {
NSString *language;
}
+ (LocalizationSystem *)sharedLocalSystem;
//gets the string localized
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)comment;
//sets the language
- (void) setLanguage:(NSString*) language;
//gets the current language
- (NSString*) getLanguage;
//resets this system.
- (void) resetLocalization;
#end
LocalizationSystem.m===
#import "LocalizationSystem.h"
#implementation LocalizationSystem
//Singleton instance
static LocalizationSystem *_sharedLocalSystem = nil;
//Current application bundle to get the languages.
static NSBundle *bundle = nil;
+ (LocalizationSystem *)sharedLocalSystem{
#synchronized([LocalizationSystem class])
{
if (!_sharedLocalSystem){
[[self alloc] init];
}
return _sharedLocalSystem;
}
// to avoid compiler warning
return nil;
}
+(id)alloc{
#synchronized([LocalizationSystem class])
{
NSAssert(_sharedLocalSystem == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedLocalSystem = [super alloc];
return _sharedLocalSystem;
}
// to avoid compiler warning
return nil;
}
- (id)init{
if ((self = [super init]))
{
//empty.
bundle = [NSBundle mainBundle];
}
return self;
}
// Gets the current localized string as in NSLocalizedString.
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)comment{
return [bundle localizedStringForKey:key value:comment table:nil];
}
// If this function is not called it will use the default OS language.
// If the language does not exists y returns the default OS language.
- (void) setLanguage:(NSString*) l{
NSLog(#"preferredLang: %#", l);
NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:#"lproj" ];
if (path == nil)
//in case the language does not exists
[self resetLocalization];
else
bundle = [[NSBundle bundleWithPath:path] retain];
[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:l, nil] forKey:#"AppleLanguages"];
}
// Just gets the current setted up language.
// returns "es","fr",...
//
// example call:
// NSString * currentL = LocalizationGetLanguage;
- (NSString*) getLanguage{
NSArray* languages = [[NSUserDefaults standardUserDefaults] objectForKey:#"AppleLanguages"];
NSString *preferredLang = [languages objectAtIndex:0];
return preferredLang;
}
// Resets the localization system, so it uses the OS default language.
//
// example call:
// LocalizationReset;
- (void) resetLocalization{
bundle = [NSBundle mainBundle];
}
#end
This code works perfectly as you mentioned.
It worked for me and that game is live now app store, if you want to check(HueShapes).
Do you by chance use NSUserDefaults to save something language related?
Look into your Simulator App directory -> Library -> Preferences -> <YourAppBundleName>.plist
See: How to force NSLocalizedString to use a specific language for description of the NSUserDefaults method of setting a language.
Perhaps you just save your language and thus detection just returns the saved value.

Resources