App Store Retrospective
Three years ago, I wrote a summary of the major problems with Apple’s App Store as an email on the iphonesb mailing list. Three years later, I think it’s a good time to look back and see how Apple has handled the situation, and assess whether we’re better off.
App Store:
Junk Apps: The App Store is filled with junk apps made in, at most, ten minutes. The proliferation of iPhone success stories has given rise to an epidemic of hopeful developers taking shortcuts hoping to make a quick buck. These apps make up 98% of the App Store’s 50k apps. This creates needless clutter that makes it hard for the real apps that real developer spent real time and real money on to get noticed.
App Store Reviews: There are plenty of problems with the reviews on the store: they’re nearly always of terrible quality. There’s no way to contest or respond to an erroneous review. There’s not even a way to respond to a review saying there’s a problem with the app with a solution, or a “that’s coming in the next update—hang tight.”
Rate On Delete: Apple is artificially creating needless bad ratings by asking users to rate an app when they delete it, which quite obviously only creates 1-star ratings. For example: iLaugh Lite 1.0 had over a third of a million users, and many of them were happy and kept coming back to the application, I could see from the analytics. Yet, my rating was 2 stars, because a couple thousands (out of a quarter of a million) users didn’t like the app and deleted it and rated it 1 star. The hundreds of thousands of happy users, though, didn’t delete the app and therefore were not asked to rate the app. This creates artificially low ratings.
Search is broken. Either you’ve got to put a whole lot of keywords in your application name, which sucks for plenty of reasons, or you’ll fall behind the ones who do. Often, you just won’t show up in search that should totally return you first. Advanced search is useless and impractical.
Top Apps Charts: These charts have so much effect on whether an application gets noticed and downloaded that whether you show up on these charts can decide the fate of your application. Which also causes the next point.
Ringtone ($0.99) Apps: As has been very well discussed by Craig Hockenberry on his furbo.org blog, app prices have become a race to $0.99. Since the charts are counted by downloads, no matter the price of the app, it means that a lower price which causes more downloads will make your app more likely to succeed.
Upgrades, Promotions, Variable Pricing: No way to offer paid upgrades, which is a HUGE problem. There’s still no way to give out copies of your app over the 50 promo codes limit, which only work in the US. You can’t do bundle promotions, discounts or anything of the sort either.
Customer Data: Your customer data is hidden. There’s no way you can promote another app by you or a paid 2.0 upgrade (since you’ll have to create a new app for that). There’s no way you can switch an app to another iTunes Connect account if it gets acquired, without losing all customers and not giving them any further updates. (Check out futuretap.com’s blog post.)
App Review:
Lack of Transparency: There is no communication between us and Apple. Apple doesn’t want communication. They specifically block communications. Emails to their support never get answered, and phone calls just tell us to email. Often, they reject apps on no grounds, or simply obscurely and vaguely referring to some TOS article that only partly applies to the situation, leaving you no way to communicate back and contest the decision.
Updates: Updates take ages to get approved. They sometimes get rejected while being only a bug-fix update to an app that got approved. (This has happened to me.) And even when they get approved, it takes forever, possibly leaving some critical bug or crash in your application and costing you tons of negative reviews and ratings. (Has also happened to me.) Garrett Murray posted a couple interesting articles on the topic.
Review Time: Apple often takes many weeks to review anything posted to them. Some of my reviews took up to three months. (No kidding!) This is just not viable. Period.
Arbitrary Rejections: There have been countless examples of arbitrary and unwarranted rejection. I have my very own. There have been countless more examples reported.
iTunes Connect: It is a piece of garbage. There is simply no other polite way to put it. It is painfully slow. It is awfully designed. For example, not too long ago I was editing the description for my application, iLaugh. I had opened iTunes Connect’s page for my application in one tab and in another tab I opened another of my application’s info for reference. When I was done, I submitted the changes and, to my horror, instead of updating the description as it should have it overwrote the info of the other application I had open in the other tab with what I had submitted in the first tab. This is just an example, but there are plenty more ways in which iTunes Connect constantly screws up. There’s no way to delete an application, or change an update’s version number. It also gave me erroneous sales reports a few days back. Overall, it easily wins as the worst web app I have ever used. Period.
If this list of complaints sounds oddly familiar, it should, because it’s surprisingly still relevant today. While Apple has made a few steps forward—for example, by getting rid of rate-on-delete—it has made an equal number of steps back. An example is the recent UDID fiasco.
But what’s even more scary, to me, than a mistake Apple might have made recently (such as its deprecation of UDIDs) is how relevant this old list of complaints remains. Remember, when I wrote this, the App Store was just one year old. I figured, they might just not have had time to get to these items, and that they’d improve as time went by. But, since then, they’ve had time to replay the entire life of the App Store thrice more, and for the most part, nothing has changed.
This indicates apathy. Apple just doesn’t care about its third-party developers. Their business, at the end of the day, is in selling hardware. They have no motivation to make it easy for developers to build a business on top of their platform. They don’t even care, it seems, about fostering innovation.
The App Store’s biggest flaw, at the end of the day, is that it is not a free market. It is not a meritocracy, and app success is slave to the whim of a corporate overlord that changes it mind without explanation more often than a 5 year old.
Disclaimer: this is my own opinion and in no way reflects my employer’s.
America is one of the most hospitable countries. The American people are genuinely nice: they give a lot to charity, they like to have fun, they smile. It’s hard to realize how much of a difference this makes until you spend some time living somewhere else. In Russia, for example, people seem sad and distant, while in South Africa the pleasantries feel much forced and status-driven, as if you were talking to a butler or a panhandler.
Only in America do tellers ask you if you’ve had a good experience finding what you needed, say “please” and “thank you,” and strike up conversations while scanning in your purchases. Only in America is it not inconceivable to share a cab or an umbrella with a perfect stranger—in fact, I did both of those things just yesterday.
What surprises me, then, is the disparity between that and the experience of actually getting here. The United States is notorious for its extremely paranoid and unfriendly security practices when it comes to travel and immigration control. Of all the countries I have travelled to or lived in (the list is quite large), the process for the United States has been and continues to be hands down the most painful I have experienced.
This realization came to me when reading Mark Vanhoenacker’s New York Times op-ed, in which he writes:
No country’s border staff is perfect, as every traveler knows. But America — a land where strangers greet one another in elevators, waiters act as if they like you, stores deploy professional greeters and government serves the people — should aim to be the best. That means a smile or “hello” as we approach every agent, a “please” and “thank you” to bookend every official request and an occasional “welcome” as we cross a secure border.
The Incredible Hypocrisy of Modern Citizens
One thing I’ve noticed about American culture from living in the USA for over two years now is that there’s a deep kind of hypocrisy running through our morals. We condemn many things for being indecent, while we allow much worse things to go under the guise of free speech. Meanwhile, individuals feel an incredible sense of entitlement when it comes to their perceived rights.
One recent example was when my social network feeds were inundated with calls to sign a petition for Facebook to add transexual options to the gender option. Now, I don’t want to get into gay-rights politics, but I am fervently against the idea that Facebook has any kind of obligation to include a feature because not doing so would offend a minority of its users.
I take issue with the idea that some people think they are entitled to the feature—that it is their fundamental right. They are not, and it is not. Should we start adding “Flying Spaghetti Monster” to the religion drop downs too, because some people want to identify with it? Point is, with any popular product, people will find something to rebel against. If not this, it would be something else. People need to realize that Facebook has a vision for their product, and that they need to be able to follow it unimpeded.
One fundamental aspect of good design is that it has been curated by somebody who knows what they’re doing, and has intimate knowledge of what they’re designing. This is why when users tell you that you should implement a feature, and that it will make your product better (and make you money), they’re most often full of crap. This is why anything on 99designs.com is crap. This is why Apple products are so great, and why Facebook beat MySpace.
Remember Henry Ford? “If I had asked people what they wanted, they would have said faster horses.”
My goal as a developer and designer is to make a product that people will love, and will make the world better. However, the way I’m going to do that is by thinking about what’s best for the majority of my users. The truth is 99.9% of users don’t care for outlying setting such as transgender identity. Transgender identity is much trickier than just adding an “Other” drop down item. If you allow it, how do you then genderize all the pronouns? Do you default to “he,” “she,” or use the combination “he/she?” Or worse, do you turn the user into an object and go with “it?” Bottom line is, it’s just not worth doing for the 0.1% of user’s feelings. They’re convinced with all their might that, surly, they deserve special treatment. But really, they don’t.
Secondly, I’m growing extremely tired of America’s prude culture. People are constantly getting offended at really silly things. Just look at video games, for example. You can’t sell a video game with “sexually suggestive” language to teenagers under 18, if you can put it on the market at all. Yet, there’s no problem with selling games to 10 year olds that vividly depicts ripping people in half.
Meanwhile, in real life, some words are prohibited and frowned upon for the sake of political correctness, while much worse sentiments are perfectly acceptable. It’s perfectly fine to go on Fox News and say “Mexican Immigrants are mostly criminals and should be deported,” or even openly be a member of the Ku Klux Klan, because it’s free speech. On the other hand, though, exclaiming “Holy Shit!” on broadcast television when the Giants score a home run will get you a class action lawsuit.
Personally, I think people should be able to say whatever they want—except for maybe hate speech. As for products, their owner have the right to design them however they want and let the general population vote with their actions. Isn’t that the founding principle of capitalism and the American Dream? Lastly, people really need to get off their high horse, when it comes to accusing everybody of discrimination.
Running a Modern Startup on PHP
I originally wrote this for the ChartBoost Blog.
In the modern world of agile startups and silicon valley, the buzz is all about Ruby, Python, and whatever the latest cool programming language or framework to come out is. Older technologies don’t get much love, and PHP especially has a bad reputation. In this post, I’m gonna go over why and how we use PHP as a modern technology, and the various other tools, techniques and development methodologies we employ to run as agilely and elegantly.
PHP
PHP is regarded as a clumsy and amateurish technology, best left to development newbies and $5-an-hour consultants. It’s almost bad enough to make me feel shame when I tell people we run on PHP. However, I don’t think this reputation is entirely deserved.
The language itself is, after Perl, the oldest language to be adopted en-mass as a web technology. Its roots are as a text pre-processor, and over the past 16 years it has evolved from that into something much broader. Many of its fault stems from the way it has evolved, rather than being designed the way it is today from the ground up.
I’m not going to argue PHP is the best language—it clearly isn’t. Frankly, it’s a mess. There’s no consistency in function and class names, even within the core library itself. The Object-Oriented features were tacked on at a later point and, while they’re getting better, are somewhat fragile. Here at ChartBoost, the core requirement is that we run on at least PHP 5.3, which introduced Late Static Bindings. Before that, building serious object-oriented code in PHP was impossible.
Even for all its faults, PHP remains a major player online, and some of the most impressive technology companies (like Facebook) are using it. PHP remains one of the fastest language to code with, deploy and execute. Lastly, while this is mostly due to personal preference, I find its C-inspired syntax to be one of the best in the web development world. Braces, parenthesis and semicolons make it extremely clear what the code is doing, as opposed to Ruby’s mess of symbols, implied parenthesis and lack of statement endings.
MVC
It’s paramount for a modern web app to run on an MVC (Model-View-Controller) architecture. Unfortunately, PHP offers very little in terms of modern and agile MVC frameworks. The big ones (CodeIgniter, Symphony, etc.) are extremely bloated and actually tend to get in your way more than help. Also, most impose their vision of what the model & database layers should look like.
Paraglide
Fortunately, one framework stands out from the pack. Paraglide is a minimalist framework that takes care of routing requests to controllers, rendering views, and little else. It offers the basics in terms of setting up the environment, providing basic helpers and organizing your code. It also works on the command line and from a shell (more on this later.)
Believe me when I say this, but Paraglide in mind-blowingly cool. It makes coding in PHP as elegant, and in some ways even more elegant, than the equivalent in Rails. It’s faster and lighter weight than Rails, but is easily extensible and works with pretty much any other code or package you throw its way.
MongoDB
Another decision core to our design ideals was the choice of MongoDB as our main model layer. Mongo is an incredibly powerful and scalable database system. It’s fundamentally different from MySQL in that it is at its core a key-value store. Mongo is so incredibly efficient that we have in fact completely skipped the usually required step of using Memcached. Mongo also offers greater reliablility and safety than MySQL with features such as failure-proof replica sets, and a querying interface that’s invulnerable to injection attacks. Avoiding SQL altogether has also been extremely pleasant. One of Mongo’s biggest advantages is easy and powerful scaling through replica sets. When a node goes down, or is added, Mongo will automatically recognize it and rebalance itself, without causing any downtime. There is no single-point-of-failure.
MongoModel
A pet project of mine has been MongoModel, and it is what we use as the third leg of our architecutre. MongoModel is an ORM which uses Mongo as its datastore, and adds features vital to a full-featured web application. It provides object-mapping, relationships, validations and is extremely elegant to use. Much like with Rails’ ActiveRecord, sensible defaults are deduced, and it’s schema-agnostic. You do not need to setup or even define what your database looks like. Rather, you just use the objects and MongoModel takes care of everything else.
Unit Testing
While we don’t practice Test-Driven-Development, we do have unit tests in place. PHP does not provide an elegant test library, so we built our own (soon-to-be open-sourced.)
Shell Development & Scripting
Paraglide is, to my knowledge, the only PHP framework that works in command-line scripts and from an interactive shell. Script functionality is extremely important in order to run cron scripts and various other maintenance and administration tasks. Interactive shell access is a boon for quick development and debugging. We use PHP-Shell to interact with our code directly from the command line. This is quite similar to Rails’ script/console.
Git
Everything we do is stored in Git. Git’s virtues are well-known within the community, so I will only say that git has been incredibly useful in keeping track of our code, the history, and for collaboration. We even use git as a wiki, to keep track of our documentation and various other internal documents.
GitHub
All our git repositories are hosted on GitHub. The main value of this, besides the hosting and gorgeous user interface, has been to use the social features to keep track of who’s been doing what. GitHub makes it really easy to have an overview of what’s happening. It also manages user accounts and rights beautifully.
Capistrano
Our main server-side code lives in a Git repository. We have dedicated branches for production code. We use Capistrano for deployments. The git repository has a dedicated branch for production code, which we merge to as we deploy stuff. A script will automatically run unit tests on anything that is pushed to production.
Amazon Web Services
ChartBoost relies on Amazon Web Services’ many products, including EC2 for cloud servers, S3 for data storage, SES for emailing and various other smaller services. This lets us pay for how much we use only, and allows for simple and fast scaling. We have an image ready to be deployed to new nodes, so we can handle any traffic thrown at our app.
Communications & Internal Tools
Last but not least, there’s the tools we use internally to keep in sync. Lighthouse takes care of our bug-tracking needs, while its companion, Tender handles support. We use Campfire for group chats, and AIM for one-on-ones. Google Apps & Gmail take care of our emails. Also check out companion Mac apps Lighthouse Keeper for Lighthouse, and Propane for Campfire.
If you read this far, you now have a good overview of the various tools and techniques we use to code agilely at ChartBoost. Even though we chose an unpopular language to base our technology on, I think it has helped us tremendously. With this post, I hope to spread the love again for PHP and these various ways of using it in a modern environment.
If you’re a graphic designer, you’re probably familiar with canons of page construction. In book design, canons of page construction help you use aesthetically pleasing and balanced text block and margins. It has been used by many typographers throughout the ages, starting with the Gutenberg bible.
Constructing them, though, is somewhat of a pain. You have to go through a long series of steps, either in illustrator or by hand, constructing the text block geometrically. So I decided to write a small web app to do it automatically. Check it out online! Currently, only the most common canon is supported, but in the future I will add any canon I find the need to construct to the project.
If you’re more of a developer, I’ve open sourced the project on github under the Azure License.
On Saturday, I attended the Compostmodern conference on sustainable design. Sustainability is a fancy buzzword used by big corporations so that they can feel socially responsible. File it away with previous contenders such as Synergy and Trickle-down, and don’t ever use it. But beyond the ill-advised terminology lies an really important concept; which is that as we design, we have responsibilities that go beyond the client’s brief and balance sheet. Sure, those should always be the primary considerations—but we also have a responsibility to our environment and society. Sustainability is at the intersection of consideration for the environment, society and economy.
In order to get the most out of the concepts from the conference, though, design must be defined as more than any or all of the professions suffixed with the word design. (Industrial, Graphic, Web, Interior, Interaction… you name it!) Design is—or should be—everything a company does. Every interaction anyone ever has with a company, good or bad, becomes part of its brand. One of the first concepts introduced by several of the speakers was that of 360° Design. With 360° Design, the challenge is to design a product or brand across all aspects of its presence, whether web, print, physical, or even the experience of using it. The concepts of sustainable design, though, apply to more than design. You can draw from it in entrepreneurship, leadership, or even as a way of life.
For me, two nuggets of wisdom stood out from the various talks. The first one pertains to getting a message effectively across through what speaker Jonah Sachs calls the Myth Gap. A successful myth is the combination of explanation, meaning and story. Explanation serves the rational mind, while meaning serves the emotional. The last element, the story, is what engages the consumer. A successful story can be further divided up in three basic elements: freaks, cheats and familiars. Freaks are human characters that are extraordinary in some way. Cheats are those don’t follow the status quo. This includes both criminals (whom the viewer is against), or rebels (whom the viewer roots for). The last element is familiars: things which viewers can relate to. It is by combining all of these elements that most of the successful stories caught traction.
Secondly, Lisa Gansky introduced attendees to the concept of the mesh. The mesh is about the sharing of experiences and physical things among people. The new wave of popular services provide access to experiences, rather than ownership of things. Netflix, for example, lets people experience movies without having to buy and own them. Zipcar, similarly, allows for on-demand access to a car without having to own a car. Airbnb provides peer-to-peer access to other members’ proprieties without having to rent a hotel room.
The overarching theme of the conference, though, was sustainability. The common perception of sustainability is that of radical green-activism such as Greenpeace. Many, including myself, find this kind of activism off-putting. Not only does it alienate me with its holier-than-thou attitude, but it often does much less good than what can be achieved through friendlier means. The key to getting people involved in a project that benefits the greater good is to incentivize the better option. Give them an alternative that does not compromise their experience of the product.
Green is not absolute. The goal should not be to have a green product, but rather a greener version of what people currently have and want. Sure, the warm feeling one gets when doing something good can be an incentive; but don’t kid yourself, people will always put their quality of lift first, and rightly so. After all, that is the whole basis of the american dream and the founding of this very nation—the pursuit of happiness. As Benjamin Franklin once said, “those who sacrifice liberty for security deserve neither.” Rephrasing his quote, I will boldly claim that those who sacrifice the pursuit of happiness for the hope of a better future deserve neither.
I Can Crack Your App With Just A Shell (And How To Stop Me)
Well, not you specifically, but by you I mean the average Mac developer. It’s too easy to crack Mac apps. Way too easy. By walking through how I can hack your app with only one Terminal shell, I hope to shed some light on how this is most commonly done, and hopefully convince you to protect yourself against me. I’ll be ending this article with some tips to prevent this kind of hack.
Disclaimer: I am fervently against software piracy, and I do not participate in piracy. Some will view this article as an endorsement of piracy, but rest assured that it is not. However, I do not believe that obscurity and ignoring the problem is an acceptable solution.
In order to follow along you’re going to need a few command line utilities. You’re going to need the Xcode tools installed. And lastly, you’re going to need an app to operate on. I chose Exces, a shareware App I wrote a long time ago.
Let’s start by making sure we have the two utilities we need: otx and class-dump. I like to use Homebrew as my package manager of choice. Note that I will use command line utilities only, including vim. If you prefer GUIs, feel free to use your code editor of choice, HexFiend and otx’s GUI app.
$ sudo brew install otx
$ sudo brew install class-dump
The first step is to poke into the target app’s headers, gentlemanly left intact by the unwitting developer.
$ cd Exces.app/Contents/MacOS
$ class-dump Exces | vim
Browse around, and find the following gem:
@interface SSExcesAppController : NSObject
{
[...]
BOOL registred;
[...]
- (void)verifyLicenseFile:(id)arg1;
- (id)verifyPath:(id)arg1;
- (BOOL)registred;
What do we have here?! A (badly spelt) variable and what looks like three methods related to registration. We can now focus our efforts around these symbols. Let’s continue poking by disassembling the source code for these methods.
$ otx Exces -arch i386
Note that Exces is a universal binary, and that we need to ensure we only deal with the active architecture. In this case, Intel’s i386. Let us find out what verifyLicenseFile: does.
-(void)[SSExcesAppController verifyLicenseFile:]
[...]
+34 0000521e e8c21e0100 calll 0x000170e5 -[(%esp,1) verifyPath:]
+39 00005223 85c0 testl %eax,%eax
+41 00005225 0f84e2000000 je 0x0000530d
[...]
+226 000052de c6472c01 movb $0x01,0x2c(%edi) (BOOL)registred
[...]
This is not straight Objective-C code, but rather assembly—what C compiles into. The first part of each line, the offset, +34, shows how many bytes into the method the instruction is. 0000521e is the address of the instruction within the program. e8c21e0100 is the instruction in byte code. calll 0x000170e5 is the instruction in assembly language. -[(%esp,1) verifyPath:] is what otx could gather the instruction to represent in Obj-C from the symbols left within the binary.
With this in mind, we can realize that verifyLicenseFile: calls the method verifyPath: and later sets the boolean instance variable registred. We can guess that verifyPath: is probably the method that checks the validity of a license file. We can see from the header that verifyPath: returns an object and thus would be way too complex to patch. We need something that deals in booleans.
Let’s launch Exces in the gdb debugger and check when verifyLicenseFile: is called.
$ gdb Exces
(gdb) break [SSExcesAppController verifyLicenseFile:]
Breakpoint 1 at 0x5205
(gdb) run
No bite. The breakpoint is not hit on startup. We can assume that there’s a good reason why verifyLicenseFile: and verifyPath: are two separate methods. While we could patch verifyLicenseFile: to always set registred to true, verifyLicenseFile: is probably called only to check license files entered by the user. Quit gdb and let’s instead search for another piece of code that calls verifyPath:. In the otx dump, find the following in awakeFromNib:
-(void)[SSExcesAppController awakeFromNib]
[...]
+885 00004c8c a1a0410100 movl 0x000141a0,%eax verifyPath:
+890 00004c91 89442404 movl %eax,0x04(%esp)
+894 00004c95 e84b240100 calll 0x000170e5 -[(%esp,1) verifyPath:]
+899 00004c9a 85c0 testl %eax,%eax
+901 00004c9c 7409 je 0x00004ca7
+903 00004c9e 8b4508 movl 0x08(%ebp),%eax
+906 00004ca1 c6402c01 movb $0x01,0x2c(%eax) (BOOL)registred
+910 00004ca5 eb7d jmp 0x00004d24 return;
[...]
The code is almost identical to verifyLicenseFile:. Here’s what happens:
verifyPath: is called. (+894 calll)
- A test happens based on the result of the call. (
+899 testl)
- Based on the result of the text, jump if equal. (
+901 je) A test followed by a je or jne (jump if not equal) is assembly-speak for an if statement.
- The
registred ivar is set, if we have not jumped away.
Since awakeFromNib is executed at launch, we can safely assume that if we override this check, we can fool the app into thinking it’s registered. The easiest way to do that is to change the je into a jne, essentially reversing its meaning.
Search the dump for any jne statement, and compare it to the je:
+901 00004c9c 7409 je 0x00004ca7
+14 00004d9f 7534 jne 0x00004dd5 return;
7409 is the binary code for je 0x00004ca7. 7534 is a similar binary code. If we simply switch the binary code for the je to 7534, at address 00004c9c, we should have our crack. Let’s test it out in gdb.
$ gdb Exces
(gdb) break [SSExcesAppController awakeFromNib]
Breakpoint 1 at 0x4920
(gdb) r
(gdb) x/x 0x00004c9c
0x4c9c <-[SSExcesAppController awakeFromNib]+901>: 0x458b0974
We break on awakeFromNib so we’re able to fiddle around while the app is frozen. x/x reads the code in memory at the given address.Now here’s the confusing thing to be aware of: endianness. While on disk, the binary code is normal, intel is a little-endian system which puts the most significant byte last, and thus reverses every four-byte block in memory. so while the code at address 0x4c9c is printed as 0x458b0974, it’s actually 0x74098b45. We recognize the first two bytes 7409 from earlier.
We need to switch the first two bytes to 7534. Let’s start by disassembling the method so we can better see our way around. Find the relevant statement:
0x00004c9c <-[SSExcesAppController awakeFromNib]+901>: je 0x4ca7 <-[SSExcesAppController awakeFromNib]+912>
Now let’s edit code in memory.
(gdb) set {char}0x00004c9c=0x75
(gdb) x/x 0x00004c9c
0x4c9c <-[SSExcesAppController awakeFromNib]+901>: 0x458b0975
(gdb) set {char}0x00004c9d=0x34
(gdb) x/x 0x00004c9c
0x4c9c <-[SSExcesAppController awakeFromNib]+901>: 0x458b3475
Here we set the first byte at 0x00004c9c. By simply counting in hexadecimal, we know that the next byte goes at address 0x00004c9d, and set it as such. Let’s disassemble again to check if the change was done right.
(gdb) disas
0x00004c9c <-[SSExcesAppController awakeFromNib]+901>: jne 0x4cd2 <-[SSExcesAppController awakeFromNib]+955>
Whoops, we made a mistake and changed the destination of the jump from +912 to +955. We realize that the first byte (74) of the byte code stands for the je/jne and the second byte is the offset, or how many bytes to jump by. We should only have changed 74 to 75, and not 09 to 34. Let’s fix our mistake.
(gdb) set {char}0x00004c9c=0x75
(gdb) set {char}0x00004c9d=0x09
And check again…
0x00004c9c <-[SSExcesAppController awakeFromNib]+901>: jne 0x4ca7 <-[SSExcesAppController awakeFromNib]+912>
Hooray! This looks good! Let’s execute the app to admire our crack.
(gdb) continue
Woot! Victory! We’re in, and the app thinks we’re a legitimate customer. Time to get wasted and party! (I recommend Vessel nightclub in downtown San Francisco.) Well, not quite. We still need to make our change permanent. As it currently stands, everything will be erased as soon as we quit gdb. We need to edit the code on disk, in the actual binary file. Let’s find a chunk of our edited binary big enough that it likely won’t be repeated in the whole binary.
(gdb) x/8x 0x00004c9c
0x4c9c <-[SSExcesAppController awakeFromNib]+901>: 0x458b0975 0x2c40c608 0x8b7deb01 0xa4a10855
0x4cac <-[SSExcesAppController awakeFromNib]+917>: 0x89000141 0x89082454 0x89042444 0x26e82414
That’s the memory representation of the code, a whole 8 blocks of four bytes starting at 0x00004c9c. Taking endianness into account, we must reverse them and we get the following:
0x75098b45 0x08c6402c 0x01eb7d8b 0x5508a1a4
0x41010089 0x54240889 0x44240489 0x1424e826
The very first byte of the series is the 74 that we switched into 75. By changing it back, we can deduce the original binary code to be:
0x74098b45 0x08c6402c 0x01eb7d8b 0x5508a1a4
0x41010089 0x54240889 0x44240489 0x1424e826
Let’s open the binary in a hex editor. I used vim, but feel free to use any hex editor at this point. HexFiend has a great GUI.
(gdb) quit
$ vim Exces
This loads up the binary as ascii text, which is of little help. Convert it to hex thusly:
:%!xxd
vim formats hex like this:
0000000: cafe babe 0000 0002 0000 0012 0000 0000 ................
The first part, before the colon, is the address of block. Following it are 16 bytes, broken off in two-byte segments. Incidentally, every Mach-O binary starts with the hex bytes cafebabe. Drunk Kernel programmers probably thought it’d be funny. Now that we have our beautiful hex code loaded up, let’s search for the first two bytes of our code to replace:
/7409
Shit. Too many results to make sense of. Let’s add another two bytes. Search for “7409 8b45” instead and boom, only one result:
001fc90: 0089 4424 04e8 4b24 0100 85c0 7409 8b45 ..D$..K$....t..E
Edit it to the following:
001fc90: 0089 4424 04e8 4b24 0100 85c0 7509 8b45 ..D$..K$....t..E
Convert it back to binary form, then save and quit:
:%!xxd -r
:wq
And… We’re done! To check our work, launch the app in gdb, break to [SSExcesAppController awakeFromNib] and disassemble.
$ gdb Exces
(gdb) break [SSExcesAppController awakeFromNib]
Breakpoint 1 at 0x4c90
(gdb) r
(gdb) disas
Admire our work:
0x00004c9c <-[SSExcesAppController awakeFromNib]+901>: jne 0x4ca7 <-[SSExcesAppController awakeFromNib]+912>
Quit gdb and relaunch the app from the Finder, and bask in your leet glory.
How you can stop me
Objective-C makes it really easy to mess with an app’s internals. Try to program the licensing mechanism for your app in pure C, that will already make it harder for me to find my way around your binary. Also read this older article of mine on three easy tips—stripping debug symbols, using PT_DENY_ATTACH, and doing a checksum of your binary—you can implement to make it a whole lot harder for your app to be cracked.
A truly skilled hacker will always find his way around your protection, but implementing a bare minimum of security will weed out 99% of amateurs. I am not a skilled hacker—yet with some very basic knowledge I tore this apart in no time. Implementing the various easy tips above takes very little time, yet would have made it enough of a pain for me that I would have given up.
The Ultimate Solution For Xcode Auto-Versioning With Git
After struggling with several suboptimal solutions for years, I have finally come to find the best Xcode versioning solution for git users. First off, tip of the hat to Marcus Zarra and Johannes Gilger for posting their solutions, which inspired me in my search for the ultimate solution.
A couple advantages that make this solution better than those I’ve used in the past:
- It’s completely filesystem independent. Save for the git binary location requirement, this would work across any Mac with no additional setup. (It should also be quite easy to edit the script to detect git using
which.)
- It works across clones and systems.
- Because the version is the current git SHA1 hash, it always refer to a specific commit you can get back to when debugging later.
- It processes the version number at every build immediately. Some of the solutions I’ve used in the past required a double-build, because of Xcode’s tendency to run scripts after the preprocessor. Not so here.
- No duplication of code in projects with multiple targets.
- Works for iPhone, Mac App Store and Mac apps.
So without further ado, my solution: I rely on an external target of type Shell Script which I call Versioning. Every other target sets Versioning as a Direct Dependency, ensuring its script is run before the preprocessor. Versioning contains the following Run Script:
cd "$PROJECT_DIR"
VERSION=`/usr/local/bin/git rev-parse --short HEAD`
cd "$PROJECT_TEMP_DIR"
echo "#define GIT_VERSION $VERSION" > revision.prefix
cd "$PROJECT_DIR"
touch Info.plist
In Info.plist, the CFBundleShortVersionString is set to GIT_VERSION. In the project’s main build settings, Preprocess Info.plist is turned on and Info.plist Preprocessor Prefix File is set to $PROJECT_TEMP_DIR/revision.prefix.
How do you want to change the world?
This essay is part two of my application to the Thiel Fellowship. It’s rare for an application essay to spark genuine reflection, but I think this helped me formulate and articulate what my beliefs and ambitions are.
Problem-solving ability is the key to making the world a better place. People need to be presented with more—and better—solutions to the challenges they encounter in daily life. Technology needs to be made easy to use and understand, and must help make people’s lives better without involving a compromise. I intend to make the world a better place by creating software that is well designed and that will have a positive effect on as many people as possible.
British philosopher and ethical theorist John Stuart Mill presented us with an interesting theory in the form of his Greatest Happiness Principle. The Principle is a doctrine by which to judge the ethicality of actions, and it states that the ethical course of action is that which will generate the most happiness for the greatest amount of people. I find that it is relevant when applied to the field of software. In that context, it states that what one should strive for is to build something which will generate great happiness for a great many people. Happiness is a loose term, in that it could refer to a game which will keep users happily entertained while waiting in line at the bank, or it could be an app or a service that can save lives, generating happiness on a much more profound level. The form does not matter, only that it has a net positive gain.
Some examples come to mind. A product that I use and respect a great deal is Mint.com. Its founders saw a deficiency in the way people dealt with their financial lives (mostly ignoring it because of extreme complexity,) and decided to harness the power of technology to come up with a radical new solution. Mint has been of tremendous help to me personally, and to millions of others. Another product I look up to is Tumblr. Taking a simple idea that already existed, Tumblr re-imagined what blogging could be, and presented their carefully redesigned solution to the world. Even though it does nothing to solve a fundamental life problem, it generates happiness and thus is a success in my books. Lastly, Instapaper is a highly underrated product from Tumblr’s ex-Lead Developer, Marco Arment. It was a side-product that Arment built to scratch his own itch, and that after polishing, he released for the public to enjoy. It has deservingly been enjoying growing success, and is a product that I use daily.
My software projects have kept this in mind. My first foray into the world of development, Exces, was an app that sought to make encryption easy to use and understand for anybody. Geared towards the novice user, the application simplified and abstracted the complexities of secure encryption by using a metaphor that people are already familiar with: bank vaults. My next big project, iLaugh, was a lighthearted iPhone app which entertained users with jokes and funnies, while being a joy to use and keeping users engaged through carefully considered design decisions. My next big project is a cloud-based notes-to-self app which lets people quickly jot down thoughts from anywhere, and deal with them later.
The feedback I have received from users show that I am already succeeding at making people’s lives slightly better. There is no better feeling than receiving an email from a happy user thanking me for my work. Though this is nowhere near the scale I envision eventually affecting people’s lives at, I see this as a step in the right direction. I moved from Switzerland to San Francisco in order surround myself with people who will make me better able to achieve that goal. I believe in taking every opportunity I get to learn something new and enrich my knowledge and world view. Because of this, I have gained various interest and hobbies, from graphic design, to languages, to software and business.
What I hope to gain from the fellowship is a network of people who share my way of thinking, and who will be able to mentor me and eventually make me better able to change the world. I’m looking for a way to kickstart my journey in the world of the startup.
Tell us one thing about the world that you strongly believe is true, but that most people think is not true.
This essay was written for my application to the Thiel Fellowship. It’s rare for an application essay to spark genuine reflection, but I think this helped me formulate and articulate what my beliefs and ambitions are.
I believe that with enough willpower and effort, anybody can change the world for the better.
While this statement may sound naive and cliché—it rings fundamentally true to me. It is the basis upon which the American Dream is built, and which has driven the United States through the past century. Unfortunately, the dream has been bastardized to the point that it is now more evocative of a fantasy than a dream.
The american political landscape—in its constant bickering and back-and-forth—tries to capture the public’s goodwill by using and glamorizing ideas that appeal to their humanity. Things like religion, freedom, and the American Dream are prime topics to drive agreement and enthusiasm. This, unfortunately, has made the few who diverge from the status quo, and who think for themselves, wary of beliefs like that.
Fundamentally, though, there is some truth to the idea that the world is a place where anybody has an opportunity to make a difference. It is not an easy thing to go against to the common wisdom, to persevere when everybody thinks you are crazy. It takes courage, self-confidence and determination. But time and time again, we have seen people do it and achieve inconceivable results—from Ghandi and Thomas Edison, to modern luminaries like Steve Jobs and Elon Musk.
Achievements do not necessarily have to be of that magnitude either. Some of the people I respect most are neither famous nor widely known. But they are people who take their craft to heart and are determined to make a difference however they can. They strive to be the best at what they do, and in so doing make a positive difference in the world. A small difference, sure, but if the majority of people thought and acted that way, it would go a long way towards alleviating the world’s problems and making it a more pleasant place to live in.
One of the qualities I respect most in people is their ability and willingness to think for themselves, and take action to back up their ideals. Think there’s a problem with an aspect of your life? Quit bitching and do something about it! (Within reason, of course.) Creativity and entrepreneurship are the basis for innovation, but they are fragile things. In today’s remix culture, it is far too easy to spend hours watching videos on YouTube, or shows on TV, or doing entertaining yet inconsequential things instead of actually creating something.
Ambition is another important factor in making a difference. When you hear Musk or Jobs talk, they will tell you about having an insane utopian vision for the world. Jobs sees a world of interconnected and designed technology, that just works and is accessible for all to use and enjoy. Musk sees a world where every car is electric, where our energy is clean and solar, and where we have expanded into space. They see their current achievements as the first step towards their lofty goals. They probably won’t achieve those goals in their lifetimes. But, in trying to get there, they have already changed the world in a major way. If you set your goal far enough, even if you only get ten percent of the way there, you’ve already accomplished something amazing.
These are all principles that I take to heart and try to apply to my life. My skills lie in software and design, and thus when I encounter a challenge in my life I try to design a better solution through software. If it is a challenge that other people may also encounter, I will polish my solution and turn it into an actual product. If, as a result, I have made a million people or just one person’s life more pleasant—I will have made a positive difference in the world. Originally from Switzerland, I moved to San Francisco by myself in order to be closer to a community of people who strive for the same thing I do. People who can enrich my knowledge and view of the world and will ultimately enable me to make an even bigger difference in the world.
One project that caught my attention this summer is Diaspora. While TechCrunch and everybody on the internet were busy complaining about their disagreement with Facebook’s privacy policy change, a group of students from NYU decided to actually do something about it. I was compelled to donate to the project, because even if the project goes nowhere, their willingness to act is something to be encouraged. My belief is that with enough willpower and effort, I will be able to make a notable difference in the world. And my hope is that many other people will too.
The more I think about it, the more I am opposed to the way in which Wikileaks and Julian Assange operate. While investigative journalism and the questioning of government are vital to a free society, I find myself increasingly convinced that Wikileaks does not constitute investigative journalism, but rather a random and harmful dump of classified information for the sake of making a political statement.
There are valid reasons for secrets and “white lies.” Without them, civilization would collapse and social interaction would become pointless. In a well-reasoned and quite convincing article, Jaron Lanier writes:
What if we come to be able to read each other’s thoughts? Then there would be no thoughts. Your head has to be different from mine if you are to be a person with something to say to me. You need an interior space that is different from mine in order to have a different, exotic model of the world, so that our two models can meet, and have a conversation.
[…]
Asking whether secrets in the abstract are good or bad is ridiculous. A huge flow of data that one doesn’t know how to interpret in context is either useless or worse than useless, if you let it impress you too much. A contextualized flow of data that answers a question you know how to ask can be invaluable.
On the argument for Wikileaks as investigative journalism:
If we want to understand all the sides of an argument, we have to do more than copy files. It’s not as though we are supporting reporters out there on the ground to do independent investigative journalism. Random leaking is no substitute for focused digging. The “everything must be free and open” ideal has nearly bankrupted the overseas news bureaus.
I don’t mean to make this a pro-government rant. Don’t get me wrong, I think there’s plenty of things that are wrong with the US government and political landscape as it is. I just think Wikileaks’ approach to the problem is neither helpful nor ethical.
Categorical Imperative
In response to an annoying philosophy assignment, I go all meta on them and write about whether to write the essay…
At this very moment, I am faced with the dilemma of whether to write this paper. The brief clearly states to use a personal dilemma and relate it to Immanuel Kant’s Categorical Imperative, walking through one’s reasoning and eventual solution. However, a college student’s life does not make a good resource for interesting dilemmas.
More importantly, though, I am morally opposed to assignments that require an essay based on personal experience for several reasons. A personal dilemma is by definition personal, ie. something that one might not necessarily want to share with one’s instructors and peers. If the aim of this paper is to demonstrate one’s understanding of Kant’s Categorical Imperative, why force students to use a personal but dubiously related dilemma, instead of using a hypothetical example that would better illustrate the concepts taught? Lastly, it puts students who have had different life experiences and have been faced with various kinds of dilemmas on unequal ground. Thus a student with a better understanding of the concept at hand but a less interesting personal history is at a disadvantage.
Kant’s Imperative states that “I should never act except in such a way that I can also will that my maxim should become a universal law.” This means that one ought to act in a way one would find reasonable, were it applied as a rule to anybody else in the same situation. Kant has a concept of goodwill, which he explains as meaning that goodness comes from the intention. Acting to fulfills one’s duty is to do good, and one’s duty is to act in act in such a manner that one would want anybody else to act, in the same circumstances. Kant emphasizes reason over emotion, when faced with an ethically difficult situation.
Faced with the dilemma which puts my will to complete the assignment as best I can against my moral objection with the form of the assignment, I need to reason objectively about which is the morally optimal solution. According to Kant, I need to act in the same way I would want any fellow student to act. Considering how I would feel if one of my fellow student were to get off easily without doing the assignment (that he is lazy, and deserving when he fails the assignment), that option is out of the question. On the other hand, I would not look up to a student who gives in and either makes up a fake dilemma, thus not following the assignment’s requirements, or uses a uncreative and boring situation from his past.
What I need is a creative solution. One that will still follow the requirements and show that I understand the concepts taught, while minimizing my moral objections. In terms of absolutes, I chose to side with the assignment against my emotional distaste for essays based on personal experience. Hopefully, though, this is a creative and witty solution to a petty dilemma.
Report on iAd
I’ve been running iAd on relatively high traffic since day one. Here’s how – for me – it’s been performing, and how it breaks down against competing ad networks.
First, the good. The eCPM is amazing. Some dude reports getting $150 eCPM on his first day on iAd. While this is mind-blowingly high and in no way representative of the average on the network, eCPMs can be expected to be quite high. My eCPM averages $10-$15, which is quite good.
Of course, we have to put these numbers in perspective. We cannot do a 1-to-1 comparaison with competing networks. Another important factor to consider: Most competing ad networks refresh their ads every 30s. iAd does it every 3min. Thus, for the time it takes iAd to display one ad, another network gets to show 6.
For a fair comparaison, we need to adjust the eCPM. Taking the above into account, let’s divide the number by 6 to get something we can compare to networks that refresh every 30s. The resulting figure isn’t really an “effective cost per thousand impressions,” but rather something more like an “effective cost per 500min of ads being displayed.”
Compared thusly, the eCPM on iAd is only worth about $1.60-$2.50. While still quite high, this is nowhere near the mind-blowing figures that have been thrown around.

Last thing to consider: fill rates. They’re are appallingly low. Though this seems to be slowly improving, they remain below 10%. Compare this with most other non-premium networks which often get you 100% fill rate. A solution would be to run iAd as a first option, and fall back to another network for failed requests. Also, I would suggest keeping the ADBannerView around even when not displayed, leaving it to refresh in the background and once it does return an ad, displaying it.

I’m sure the fill rates will improve over time, and that iAd wil become a worthy competitor over time. Right now though, the reality is iAd generates less revenue than my previous first option, Google AdSense for mobile.
Update: Greg Yardley rightly calls me out on mistakenly stating Apple’s figures included their 40% cut. Article updated accordingly.