Market License - Easy Implementation to Protect Your Apps

neubanks89

Member
Joined
Dec 16, 2009
Messages
130
Reaction score
0
I just finished license protecting all my apps using Google's relatively new license service. I've developed an easy way to manage this in all your applications without having to mess with any of your apps actual source code.

Before you start, Make you have included the license library in your project as explained here:
Licensing Your Applications | Android Developers

1. Make a new Activity in your project called LicenseCheck.java

2. Paste the following in that activity:

Code:
/**
 * @author Nick Eubanks
 * 
 * Copyright (C) 2010 Android Infinity (http://www.androidinfinity.com)
 *
 */
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.widget.Toast;

import com.android.vending.licensing.AESObfuscator;
import com.android.vending.licensing.LicenseChecker;
import com.android.vending.licensing.LicenseCheckerCallback;
import com.android.vending.licensing.ServerManagedPolicy;

/**
 * NOTES ON USING THIS LICENSE FILE IN YOUR APPLICATION: 
 * 1. Define the package
 * of you application above 
 * 2. Be sure your public key is set properly  @BASE64_PUBLIC_KEY
 * 3. Change your SALT using random digits 
 * 4. Under AllowAccess, Add your previously used MainActivity 
 * 5. Add this activity to
 * your manifest and set intent filters to MAIN and LAUNCHER 
 * 6. Remove Intent Filters from previous main activity
 */
public class LicenseCheck extends Activity {
	private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
		@Override
		public void allow() {
			if (isFinishing()) {
				// Don't update UI if Activity is finishing.
				return;
			}
			// Should allow user access.
			startMainActivity();

		}

		@Override
		public void applicationError(ApplicationErrorCode errorCode) {
			if (isFinishing()) {
				// Don't update UI if Activity is finishing.
				return;
			}
			// This is a polite way of saying the developer made a mistake
			// while setting up or calling the license checker library.
			// Please examine the error code and fix the error.
			toast("Error: " + errorCode.name());
			startMainActivity();

		}

		@Override
		public void dontAllow() {
			if (isFinishing()) {
				// Don't update UI if Activity is finishing.
				return;
			}

			// Should not allow access. In most cases, the app should assume
			// the user has access unless it encounters this. If it does,
			// the app should inform the user of their unlicensed ways
			// and then either shut down the app or limit the user to a
			// restricted set of features.
			// In this example, we show a dialog that takes the user to Market.
			showDialog(0);
		}
	}
	private static final String BASE64_PUBLIC_KEY = "PLACE YOUR BASE KEY FROM GOOGLE HERE";

	private static final byte[] SALT = new byte[] { INPUT 20 RANDOM INTEGERS HERE };
	private LicenseChecker mChecker;

	// A handler on the UI thread.

	private LicenseCheckerCallback mLicenseCheckerCallback;

	private void doCheck() {

		mChecker.checkAccess(mLicenseCheckerCallback);
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Try to use more data here. ANDROID_ID is a single point of attack.
		String deviceId = Secure.getString(getContentResolver(),
				Secure.ANDROID_ID);

		// Library calls this when it's done.
		mLicenseCheckerCallback = new MyLicenseCheckerCallback();
		// Construct the LicenseChecker with a policy.
		mChecker = new LicenseChecker(this, new ServerManagedPolicy(this,
				new AESObfuscator(SALT, getPackageName(), deviceId)),
				BASE64_PUBLIC_KEY);
		doCheck();

	}

	@Override
	protected Dialog onCreateDialog(int id) {
		// We have only one dialog.
		return new AlertDialog.Builder(this)
				.setTitle("Application Not Licensed")
				.setCancelable(false)
				.setMessage(
						"This application is not licensed. Please purchase it from Android Market")
				.setPositiveButton("Buy App",
						new DialogInterface.OnClickListener() {
							@Override
							public void onClick(DialogInterface dialog,
									int which) {
								Intent marketIntent = new Intent(
										Intent.ACTION_VIEW,
										Uri.parse("http://market.android.com/details?id="
												+ getPackageName()));
								startActivity(marketIntent);
								finish();
							}
						})
				.setNegativeButton("Exit",
						new DialogInterface.OnClickListener() {
							@Override
							public void onClick(DialogInterface dialog,
									int which) {
								finish();
							}
						}).create();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		mChecker.onDestroy();
	}

	private void startMainActivity() {
		startActivity(new Intent(this, MainActivity.class));  //REPLACE MainActivity.class WITH YOUR APPS ORIGINAL LAUNCH ACTIVITY
		finish();
	}

	public void toast(String string) {
		Toast.makeText(this, string, Toast.LENGTH_SHORT).show();
	}

}

3. Change the Base Key to the one google provided, Place 20 random integers in the SALT, Change MainActivity.class to the Main Activity of your application.

4. Update your Manifest File with the new activity
Code:
<!-- Old Launch Activity Here -->
<activity android:label="@string/app_name" android:name=".MainActivity" />
<!-- New License Launch Activity with all intent filters from your previous main activity -->
<!-- Translucent.NoTitleBar is so that this activity is never shown to the user -->		
<activity android:label="@string/app_name" android:name=".LicenseCheck"
			android:theme="@android:style/Theme.Translucent.NoTitleBar">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>

5. Add Permission In the manifest Tag but not in the application tag
Code:
    </application>
	<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
</manifest>



You're all done! Make sure you test it out before publishing.
 

Aidwe

New Member
Joined
Nov 23, 2010
Messages
1
Reaction score
0
Fantastic implementation thanks.

One comment though:
It wouldnt be that hard to hack a way around this. All they'd have to do is launch the MainActivity. There are ways to do this (quite easily) on some alternate homes. I would recommend setting a variable on success.

But as i said. Great thanks :)
 
OP
N

neubanks89

Member
Joined
Dec 16, 2009
Messages
130
Reaction score
0
Fantastic implementation thanks.

One comment though:
It wouldnt be that hard to hack a way around this. All they'd have to do is launch the MainActivity. There are ways to do this (quite easily) on some alternate homes. I would recommend setting a variable on success.

But as i said. Great thanks :)

yeah, that is something I should've mentioned. This is just a quick way to get started. I recommend using the above as your main activity and make any other other activity have a requirement that it is called from that main/license activity (can easily do this via intent bundles). Now that Android 2.3's sdk automatically obfuscates your code for you, hacking apps is going to be quite a bit tougher on Android thankfully. I've already seen pirated versions of my app "Grades: Student Organizer" go down from 65% of all users not being licensed to only 40% in two months of implementing the LVL and all of those 40% are using a very outdated version.
 

amasson

New Member
Joined
Dec 13, 2010
Messages
3
Reaction score
0
Location
Montréal, Québec, Canada
Thanks a lot for sharing this sample. This helped me implementing a protected version of my Android app.

One thing I noticed and maybe worth to be shared here: if you implement this and just deploy the apk directly onto the phone for testing, you will just receive the error message ERROR_NOT_MARKET_MANAGED inside the applicationError(ApplicationErrorCode errorCode) method. However one your app is uploaded and available through Android Market, then the license check will proceed ok.
 

alostpacket

Member
Joined
Dec 7, 2009
Messages
67
Reaction score
0
Thanks a lot for sharing this sample. This helped me implementing a protected version of my Android app.

One thing I noticed and maybe worth to be shared here: if you implement this and just deploy the apk directly onto the phone for testing, you will just receive the error message ERROR_NOT_MARKET_MANAGED inside the applicationError(ApplicationErrorCode errorCode) method. However one your app is uploaded and available through Android Market, then the license check will proceed ok.

-edit-
check this link:

http://groups.google.com/group/andr...read/thread/b83cc702603b0ee3/74a2ed31e4c11111
 
Last edited:
OP
N

neubanks89

Member
Joined
Dec 16, 2009
Messages
130
Reaction score
0
Thanks a lot for sharing this sample. This helped me implementing a protected version of my Android app.

One thing I noticed and maybe worth to be shared here: if you implement this and just deploy the apk directly onto the phone for testing, you will just receive the error message ERROR_NOT_MARKET_MANAGED inside the applicationError(ApplicationErrorCode errorCode) method. However one your app is uploaded and available through Android Market, then the license check will proceed ok.

My development practice regarding this issue is to always device test a new version of an app with the current version number (not the user-identifible number but the incremental one). That way the response is exactly like that of a current user. Then make sure you increment that number when you make your final build. Testing that one on a device will require your publisher account's device or will depend on how you handle the not market managed error.
 

alostpacket

Member
Joined
Dec 7, 2009
Messages
67
Reaction score
0
Also it should be noted this is not a very secure way of using LVL. Google recommends you edit the classes themselves to obfuscate where the calls are made and how the responses are handled. basically changing some of the default values/constants and moving the functions around so it's harder to find where you make the license check.

The system (as it's posted in tutorials) is circumvented by changing one easy-to-find switch statement in the decompiled bytecode.


You then will additionally want to use something like proguard to further obfuscate.

Finally the most secure method would be to then send the response you get from the market app to a server you control in an encrypted and signed format to process the validity of the response.

This makes more sense if your app has assets it uses from the web.

Thwarting pirates is no small task. The question becomes, how much do you want to put into it, and how much do you think you can convert those pirates into customers.
 
OP
N

neubanks89

Member
Joined
Dec 16, 2009
Messages
130
Reaction score
0
Also it should be noted this is not a very secure way of using LVL. Google recommends you edit the classes themselves to obfuscate where the calls are made and how the responses are handled. basically changing some of the default values/constants and moving the functions around so it's harder to find where you make the license check.

The system (as it's posted in tutorials) is circumvented by changing one easy-to-find switch statement in the decompiled bytecode.


You then will additionally want to use something like proguard to further obfuscate.

Finally the most secure method would be to then send the response you get from the market app to a server you control in an encrypted and signed format to process the validity of the response.

This makes more sense if your app has assets it uses from the web.

Thwarting pirates is no small task. The question becomes, how much do you want to put into it, and how much do you think you can convert those pirates into customers.

from my experimenting with using LVL it is actually better not to use it at all for my sales. About 60% of my users are using unauthorized versions of my app, all having a much more secure version of the licensing that you see above and obfuscated with pro guard. When I used LVL my return rate was about 40% and without it dropped down to around 20% because the app performed faster and never gave the user a failed license check when it actually was valid (which happens too frequently and confuses users that just purchased the app). My piracy rate did not decline no matter what prevention I used so I figured it was better for my paying customers just to not worry with it.
 

jeffv2

Developer
Developer
Joined
Dec 23, 2010
Messages
1,240
Reaction score
0
Location
south jersey
I'm not saying I would do this, I'm just wondering if one could over come this by decompiling the dex file with smali and baksmali?
I hope not..

Sent from my Droid using Tapatalk
 

alostpacket

Member
Joined
Dec 7, 2009
Messages
67
Reaction score
0
Also it should be noted this is not a very secure way of using LVL. Google recommends you edit the classes themselves to obfuscate where the calls are made and how the responses are handled. basically changing some of the default values/constants and moving the functions around so it's harder to find where you make the license check.

The system (as it's posted in tutorials) is circumvented by changing one easy-to-find switch statement in the decompiled bytecode.


You then will additionally want to use something like proguard to further obfuscate.

Finally the most secure method would be to then send the response you get from the market app to a server you control in an encrypted and signed format to process the validity of the response.

This makes more sense if your app has assets it uses from the web.

Thwarting pirates is no small task. The question becomes, how much do you want to put into it, and how much do you think you can convert those pirates into customers.

from my experimenting with using LVL it is actually better not to use it at all for my sales. About 60% of my users are using unauthorized versions of my app, all having a much more secure version of the licensing that you see above and obfuscated with pro guard. When I used LVL my return rate was about 40% and without it dropped down to around 20% because the app performed faster and never gave the user a failed license check when it actually was valid (which happens too frequently and confuses users that just purchased the app). My piracy rate did not decline no matter what prevention I used so I figured it was better for my paying customers just to not worry with it.


Thanks for this, I had been wondering myself as to whether to use it. I just got hit with my first pirate attack after having my first paid app out only a 3 weeks (my other app is free with a donate version so no pirates there). So I've been reading a lot about it lately.

I think I'm leaning against using it for the same reasons you state and it's nice to hear opinions of those who have tried it. So thanks for your post :)

It's hard though -- I have less that 35 purchases and these guys are stealing already. It's pretty hard to stomach as my hope is to pay the rent with app development.
 

alostpacket

Member
Joined
Dec 7, 2009
Messages
67
Reaction score
0
I'm not saying I would do this, I'm just wondering if one could over come this by decompiling the dex file with smali and baksmali?
I hope not..

Sent from my Droid using Tapatalk


Yes it's not infallible from what I understand. The idea is to make it so much of a pain as to not be worth the time.

However, using those techniques combined with having your app contact a private server you control with encrypted and signed responses from the license api is supposed to be very difficult to overcome.

This is especially the case if your app requires content from your server. This is because the server can refuse to serve content to the app if it detects problems. This is in contrast to having the content protection in the app itself where the user or a maclious hacker/pirate can modify it.

The old adage in terms of this kind of security is "never trust the client" (ie the App) since it's not under your control.

This is a seriously involved thing to program though. And a lot of effort for people who might not buy your software regardless. For me personally too, my PHP is so rusty I would need a tetanus shot just to get started coding anything server side.
 

jeffv2

Developer
Developer
Joined
Dec 23, 2010
Messages
1,240
Reaction score
0
Location
south jersey
I'm not saying I would do this, I'm just wondering if one could over come this by decompiling the dex file with smali and baksmali?
I hope not..

Sent from my Droid using Tapatalk


Yes it's not infallible from what I understand. The idea is to make it so much of a pain as to not be worth the time.

However, using those techniques combined with having your app contact a private server you control with encrypted and signed responses from the license api is supposed to be very difficult to overcome.

This is especially the case if your app requires content from your server. This is because the server can refuse to serve content to the app if it detects problems. This is in contrast to having the content protection in the app itself where the user or a maclious hacker/pirate can modify it.

The old adage in terms of this kind of security is "never trust the client" (ie the App) since it's not under your control.

This is a seriously involved thing to program though. And a lot of effort for people who might not buy your software regardless. For me personally too, my PHP is so rusty I would need a tetanus shot just to get started coding anything server side.

good hopefully at some point we can put an end to pirating apps.... but i doubt it
 

gflam

Developer
Developer
Theme Developer
Joined
Jun 14, 2010
Messages
1,900
Reaction score
29
Location
Jersey
It was mentioned earlier that you could just work around this by starting another activity in terminal or even some launchers that can just launch different activities however i'm thinking that in my case at least my app uses a list view which is read from my server that if i were to implement this method in that activity then opening any other activity would be pointless since it wouldn't have my server files. Just a thought for anyone whos app that uses stuff from a server
 
Top