More detailed explanation (for smaller version, check TL;DR in the end):
I created a framework that I made available in Github. In it, there are 7 Macros that define how the framework should be compiled. I can't just replace them with static const's because 6 of those Macros are used to remove some things from the framework in compiling time.
Those Macros may be divided in groups:
Group 1) Use framework when available
#define USE_THE_METAL_FRAMEWORK_WHEN_AVAILABLE true
#define USE_THE_OPENGL_FRAMEWORK_WHEN_AVAILABLE true
Those defines make the framework use the Metal and OpenGL frameworks even if they weren't added to the project. That's done using dlopen. More details below:
https://stackoverflow.com/a/24266440/4370893
https://stackoverflow.com/a/21375580/4370893
https://stackoverflow.com/a/1354569/4370893
I use this so those frameworks can be used only if they are available, so the dev can build his/her app for macOS 10.6 and still use Metal if it's available in the system. The problem is: if your app binary calls dlopen somewhere, you can't add it to the Apple Store (even if you don't really use the function that uses it), so users should be able to remove that from the code if they want. That removes some of the framework functionalities, but make it capable of being submitted.
Group 2) I'm importing the framework
#define IM_IMPORTING_THE_METAL_FRAMEWORK false
#define IM_IMPORTING_THE_OPENGL_FRAMEWORK false
Those defines make the framework really use Metal and OpenGL frameworks, without using dlopen. In that way, it can be submitted to the Apple Store and still use the features that were mentioned before.
Group 3) Behaviour changes
#define USER_NOTIFICATIONS_SHOULD_SHOW_A_BIGGER_ICON true
#define NSDEBUGLOG_SHOULD_PRINT_TO_A_DESKTOP_FILE_TOO true
The first one can also cause problems in the Apple Store, and the second one is only for debugging purposes.
And at last...
Group 4) Should be released in the Apple Store
#define I_WANT_TO_BE_RELEASED_IN_APPLE_STORE false
That one is used for that:
#if I_WANT_TO_BE_RELEASED_IN_APPLE_STORE == true
#define USER_NOTIFICATIONS_SHOULD_SHOW_A_BIGGER_ICON false
#define USE_THE_METAL_FRAMEWORK_WHEN_AVAILABLE false
#define USE_THE_OPENGL_FRAMEWORK_WHEN_AVAILABLE false
#endif
It automatically disables the three defines that may cause problems with the Apple Store.
Still, those who use Travis-CI for example are not capable of changing the framework unless they completely copy it to the project. To solve this problem (or at least reduce it), 5 of those 7 defines could be changed by a file in the main project, for example. If anyone has other suggestions that may solve the issue, I'm listening.
(Optional) If this could be done in a way that the user may be warned (by Xcode or by an error in compiling time) that he/she needs to set those variables that would be great, since this would avoid problems with users that may need to set those Macros.
TL;DR: Is it possible to change the values of some Macros within a framework without modifying the framework itself?
No. Make some method, that initialize yours module with some parameters. But anyway you are not able to use dlopen.
Related
I have a question regarding modifying SDK settings from the application using it.
I have a custom SDK which prints to a log when doing certain things. I want users of this SDK to be able to turn logging on/off. I use NSLog as my logger.
I have tried setting a preprocessor macro for this, as explained numerous times here in stackoverflow by doing something like:
#ifdef SHOWLOG
# define SLog(fmt, ...) NSLog((fmt), ##__VA_ARGS__);
#else
# define SLog(...)
#endif
And then expecting the user to set a compile flag called "-DSHOWLOG"
This does not work since the person who is using the SDK will have a compiled version of the SDK.
What other ways are there are there of changing configurations of SDK from the application that uses it?
Are environment variables a good option?
Maybe I'm misunderstanding the question, so I'll guess and offer this answer in the hope it helps:
Inside your SDK create your settings which you query to control how your SDK behaves. These settings can be stored in a shared/singleton object, behind a functional (i.e. C functions) API, or even just SDK (not publicly documented) global variables.
Provide a public API which alters the values of these settings.
So in the situation mentioned in the question you need an SDK-internal "show log" setting and a public API to set it.
HTH
I am working with an iOS project that uses
#if defined (FREE_VERSION)
The project has two targets, each of a free and paid version. If I run the free, the code under the above statement fires, and if I run the paid, the "else" code fires. That's all as expected.
But when I search the project for "FREE_VERSION" or "#defined", I get no results (other than the "#if defined" statements). So where might FREE_VERSION be defined?
I'm not very familiar with pre-processor directives and #define, so I may not be using the right terminology here. But, any help would be appreciated!
With defines like this, I assume you will find it in project settings -> build settings -> preprocessing.
The idea behind it is that you have several schemes/build targets which behave differently and you don't have to change code to setup your build, you just switch between schemes/targets.
DEBUG is an example of a similar macro, which is defined by default for Debug configuration.
That's up to you to #define it somewhere. You could have a constants header file and define it there.
In free version:
Global.h:
#define FREE_VERSION
Then you can comment it out later when compiling paid version.
In my application i have worked for some part of the next version implementations,
So we need to prevent those some implementations for this version.
our superiors asked me to do with pre macro processor, having #ifDef, endif like that and you need to define the version number in buildsettings as preprocessor macro
I have added a user defined setting 'App_Version' in build Setting,
"
How do i use it like
#ifDef AppVersion 1.0
NSLOG (current version implementation)
else
NSLOG (NEXT version implementation)
Actually i was not much awair on it, so that my interpretation was poor
#if App_Version == 1.0
I don't remember exactly how new preprocessor macros are defined in XCode but I don't think that you are defining it correctly. See How do I define preprocessor macros in Xcode 4?
However, using preprocessor macros for such things is far from recommended.
You should
Create a method that will read the application version from the bundle
Call the method to check version and use standard if-elseif-else
With iPhone development I can't really imagine why would you use such a macro/method though. How many versions of an app do you want to build? All the applications I have implemented for iOS needed only 1 version - the latest one. I don't see any reason why would you like to build an older application version from current code.
Use a versioning system and if you are implementing features for the next version into current code, use a dedicated branch! Otherwise it's just a mess and your supervisor is ... not smart ... for not seeing it.
The compiler doesn't need (or want) to know anything about application version (it is information for the application submission process). This should not be done with preprocessor macros, unless you want to define your own (even then, I wouldn't recommend it). You should check the application version at runtime
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleVersion"];
and prevent new features from being accessible in the current version based on that. More generally I would consider using branching models like git flow to handle things like that. What will happen when you have 3,4,5 ... versions to handle. The preprocessor macros will become a nightmare to manage.
Suppose that you need to have a series of complex configuration for a series of different Objects. This configuration can be NSString like service's server address, NSNumber like timeout time and so on. I want to impose the following constraint:
Configuration must not be visible and/or editable to user so plist in the app bundle is not an acceptable solution
I need different configuration value for debug, AdHoc or release target (I will likely achieve this with a series of #ifdef but i write for sack of completeness)
All the object that i want to configure belongs to a Static Library and i want to inject the configuration from the code that use this static library (inject is in italic as my personal hint to a dependency injection)
My question is: what's the best way to achieve this? Have you ever faced a problem like this? how did you solve it?
Take advantage of Xcode build system. You can have Xcode configuration files for any build configuration (Debug, Release, etc) and target combination.
These files support inheritance to simplify sharing configuration. There is no a single way to define the options. A simple one is to use GCC_PREPROCESSOR_DEFINITIONS or CFLAGS or whatever your compiler will take.
TargetX.xcconfig:
GCC_PREPROCESSOR_DEFINITIONS = STAGE=2 SERVER_URL='www.wuonm.com'
With configured values is fairly trivial to build the required NSString or NSNumber objects. Tip: you will require some kind of stringification.
It's quite similar to using #define but IMHO it's much cleaner and structured if you invest some time understanding how xcconfig files work.
If these values are constant for each build configuration, why not put them in your code as constants? I don't really see why you need to externalize these settings if they are constants that vary by config. Your ifdef selection of the settings would be just as effective (and faster) with these constants declared in a private header.
If these settings somehow change during the running of the app (not from build to build but within the confines of running a single build of the app) then externalizing these settings would be appropriate.
There are many questions that arise with this post:
Why do you need this level of protection on your input configuration?
Is the point that you dont wish the user to be able to modify your configuration, or is it that you dont wish them to be able to KNOW it at all?
If you beleive the theory that SW cannot protect SW, then truly what level of pain are you willing to put yourself through in order to put the hacker through enough pain they dont want to hack your SW (because lets face it, thats all you are really going to get, see my comments above about encryption)
I somewhat agree with getting your configuration from the internet, but this only will work IFF you are expecting an internet capability present in order to use your app. If you do this and you also want some level of protection then you are going to have to use some secure protocol (Like SSL) for downloading it.
If you truly wish your app to be able to be used locally (without internet access), then I would say that true security of your configuration is simply immposible, as you dont control the OS, your dont control the HW your SW is running on, etc...
Putting your app on the iPhone probably means that you are hoping for a fairly large amount of downloads. This means the amount of hands touching it and potentially being able to take a CRACK at it are also large. The only way to gain security in this case (as no SW really is), is to make the value of hacking it to get your configuration not worth all that much. If the value is low, then no hacker will care to try and crack it!
I faced this same issue with server URLs used in different classes and constant numbers like colors, sizes and pretty much any configuration needed to be easily changed. The best solution I found was creating a header file like Constants.h, the content of this header file was:
#define MAX_DIST 1000
#define MIN_DIST 300
#define ANIMATION_DURATION 0.010
#define PIXEL_MOVES 7
#define SENSITIVITY 14
#define VIEW_ANGLE 30 //Range of vision divided by 2
#define RAD_POS_X 415
#define RAD_POS_Y 15
#define BUTTON_VIEW_WIDTH PIXEL_MOVES*360
#define SCREEN_HEIGHT 480
#define BUTTON_WIDTH_CLOSE 180
#define BUTTON_HEIGHT_CLOSE 100
#define BUTTON_WIDTH_MEDIUM 100
#define BUTTON_HEIGHT_MEDIUM 60
#define BUTTON_WIDTH_FAR 60
#define BUTTON_HEIGHT_FAR 40
#define BUTTON_Y_POSITION_CLOSE 200
#define BUTTON_Y_POSITION_MEDIUM 135
#define BUTTON_Y_POSITION_FAR 90
#define SERVICE_URL #"http://my.server.com/SoapServer/SoapServiceWS"//
you can even define common functions like
#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)
This way i have been able to configure every aspect of my app from one single file and you can use #ifdef like you said to manage different compilations. Of course, you must import the file Constants.h in any class that will need to access any of these constants.
Your static library should provide some interface to set current configuration that will be set in runtime.
SetCurrentConfiguration(Configuration config);
Then "client" can configure it with needed configuration in runtime.
Second step is load configuration from specified source, let's say xml file and inject it to static library via method above.
If you don't want users to modify configuration files you can use some sort of encryption and then decrypt on the fly.
Require that for the first time use, an internet connection is present and download via HTTPS your config and keep it in memory (encrypted if you want).
Then take advantage of the keychain in iOS, and store your config as a string there (essentially serialize it).
NSString *serializedConfig = ...;
[keychain setObject:serializedConfig forKey#"MyConfig"];
On subsequent use, retrieve the config stored in the keychain:
NSString *serializedConfig = [keychain objectForKey:#"MyConfig"];
You can find the keychain wrapper and documentation on the Apple Docs site.
After a search here on the forum I found a question like that, and it redirected me to a tutorial which gave em some basic instructions on manipulating SpringBoard with CapitainHook.
To start I'd like to do it with normal %hooks only. Any hint where I could start?
This little introduction is meant for whoever has a minimal knowledge on Objective-C and knows what he is doing.
NOTE: I will refer to the theos install path as $THEOS. This could be ~/theos, /var/theos, /usr/theos... Yeah.
The most popular way of creating MobileSubstrate extensions, also known as tweaks, is using Dustin Howett's theos build suite. Details follow:
What is theos?
So, we should start with what theos is not:
The Operating System
A Greek God
A compiler
And of course, what theos doesn't do:
Teaches you how to code.
Creates tweaks without having you to think
Sets up a whole building environment and/or installs the iOS SDK.
Theos is a cross-platform suite of development tools for managing, developing, and deploying iOS software without the use of Xcode, featuring:
A robust build system driven by GNU Make, which makes its Makefiles easily deployable through everywhere with theos installed too.
NIC, a project templating system which creates ready-to-build empty projects for varying purposes.
Logos, a built-in preprocessor-based library of directives designed to make MobileSubstrate extension development easy and with optimal code generation.
Automated packaging: Theos is capable of directly creating DEB packages for distribution in Cydia, the most popular mean of package distribution in the jailbreak scene.
How to install theos?
On OSX: Have the iOS SDK installed and follow these instructions.
On iOS: Install the BigBoss Recommended Tools package from Cydia and run installtheos3.
On Linux: Find a mean to have the toolchain installed, and follow these instructions.
On Windows: Nothing is impossible, but if you actually manage to do so, please let me know. :P
How to use theos?
This is a very asked question and too vague. Since theos is a whole suite of development tools, it doesn't make sense to ask How to use it, but more specifically, to ask How to create software using theos.
First of all, always have the Theos Makefile Reference in hand. It covers the basics of creating a theos Makefile, and that includes solving your linking issues adding a framework or private framework to the project.
Now, you can either create your own Makefile from scratch, create your little theos clone/symlink and start coding, but theos makes this step easier. You can just use nic.pl.
A very simple example of running NIC to create something can be found here. It's very straight-forward and sets you up right-away for programming.
Now, here's where we start getting back to topic.
Creating a tweak with theos
First of all, do not run NIC when inside $THEOS/bin. NIC will create the project directory exactly where you're running it from, and it avoids any project being created in $THEOS/bin. Therefore, you'll end up with a simple error which can be avoided by creating the project directory somewhere decent.
Run $THEOS/bin/nic.pl and choose the iphone/tweak template. You will be prompted by simple information which you may well know well how to answer, except for the last field: MobileSubstrate bundle filter.
Since a big part of MobileSubstrate is not just the hooker (the library which switches original methods/functions with yours), but also the loader (the part which gets your hooking to be inserted into certain processes), you have to supply this basic information for the Loader to know where to load your tweak. This field is but the bundle identifier for the application where this project will be inserted.
com.apple.springboard, the default option is the bundle identifier for SpringBoard, the application which is:
The iOS Homescreen
The launcher/displayer of common applications
The iOS Status Bar
Handler of some high-level essential background processes
Therefore, there's where many tweaks take place, altering behavior from something as trivial as app launching to something like how the whole homescreen UI looks like.
Programming a tweak with Logos
Now, the directory generated by NIC will contain:
The Theos Makefile, where you'll change information related to compiling
The control file, where you'll change packaging-related information
A symbolic link (or shortcut) to $THEOS named theos/
The main code file, defaulted as Tweak.xm. It is already added to the Makefile for compiling, so you can start coding right-away with it!
On knowing what to do
Now, you don't have SpringBoard's source code laying around, and you can't guess what methods to hook from nowhere. Therefore, you need a SpringBoard header set. For that, you need to use a tool named class-dump-z and run it into the SpringBoard binary (which is inside the iOS filesystem) to obtain header files including all class declarations and its methods inside the application.
From that (a deal of guessing and logging a method call is involved) you can start messing around with what you want in a tweak.
Of course, if you are not hooking SpringBoard you can use class-dump-z as you would in other binaries, such as UIKit, MobileSafari, etc.
Note that for when reversing App Store apps, they'll be encrypted. You'll need to decrypt those (I am unfortunately not allowed to tell you how-to), and then just run class-dump-z on them.
On obtaining private headers
Stuff like preference bundles require the headers for private frameworks, in that case the Preferences framework's headers. Else you'll get endless missing declaration errors (as I guess you could assume).
Getting them has the same logic applied the previous step. Run class-dump-z on, at this case, the Preferences binary and throw the headers at your INCLUDEPATH. The INCLUDEPATH is where the compiler will go looking for headers you include like #include <stdio.h>. Yes, stdio.h is inside one of the directories which build a compiler's INCLUDEPATH!
When compiling with a theos Makefile, $THEOS/include counts as part of your INCLUDEPATH, which means, you can just throw your dumped headers over there and include them later.
(Note that class-dumped headers aren't always perfect, so you're likely to have a couple of header-related compilation errors which can be easily fixed with something like removing a #import directive or changing it, or adding a couple of declarations.)
Code tips
You can't link against SpringBoard, so whenever you require a class from SpringBoard you have to use either the Logos %c directive or the objc_getClass function, as defined at <objc/runtime.h> to get it. Example: [%c(SBUIController) sharedInstance], [objc_getClass("SBUIController") sharedInstance].
When not knowing what a method does or how something works in SpringBoard, try disassembling it with IDA or others. I use IDA Demo (<- noob!) for my disassembling.
Looking at example code is amazingly helpful for both learning and figuring out how something works inside SpringBoard or others (again..). Great people at GitHub to have a projects looked at are rpetrich, chpwn, DHowett, EvilPenguin, and of course way more.
To also find about how SpringBoard and other works (...), have a look at a class's article at the iPhone Dev Wiki!
Epilogue
Wait, where's the good part? Where do I learn about coding in Tweak.xm?
Well, the original question was actually How to start MobileSubstrate tweaks programming?. You're all setup, hopefully with all headers placed, ready to type in make and see your project magically compiled with theos.
All you need to do is now to actually dig into your headers or your disassembly and go hooking, calling, etc.!
Logos Reference contains exactly how to hook and use other features of Logos, and the MobileSubstrate article on the devwiki is also a great read.
In case there is any doubt, don't hesitate joining the irc.saurik.com #theos IRC channel. It's a great way to discuss theos-related topics and ask questions. I'm mostly there, along with other greatly smart people ;)
You are looking for Theos created by DHowett.. Theos allows you to make tweaks, but it doesn't give you everything you need. You don't get every header for iOS, so you have to class-dump-z the frameworks/private-frameworks from the iOS SDK. Get started here: http://iphonedevwiki.net/index.php/Theos/Getting_Started, or join irc.saurik.net #theos for more help. You can also look at my projects that use theos: https://github.com/evilpenguin
You sound like you're looking for theos. Take a look at this, it should help get you started.