19
Conquering the mysterious userId (Fixing Android Market downloading)

Originally I found the easiest way to download applications was just spoofing the final request. This worked – and worked well for the entire time I had been using it. Though it bugged me that I couldn’t find out where the actual userId was coming from. So I keep digging and digging and finally came up with the solution which is posted below. I never really shared this though, since as I said – other people just used the hack and didn’t seem to care much. The main concern people had was “how do I get it faster” – which to that I always replied “learn whats going on”. Plenty of people figured it out, though now, according to the the emails I’ve been getting recently and the ones on the Android-Market-Api Google Group, this stopped old hack has working. So, I guess it’s about time to spill the rest of the mysery.

The requests originally looked like this when being sniffed over the wire;

http://android.clients.google.com/market/download/Download?userId=###&deviceId=###&assetId=###

Though recently they started looking like this;

http://android.clients.google.com/market/download/Download?packageName=com.package.name&versionCode=##&token=###

Well – this didn’t matter much to me since I don’t use that project, but I’ve helped enough people through side channels that I figured the information is as good as public. So I’ve decided to write up a little “what is actually going on”.

Just a little while ago I committed an updated market.proto with the necessary fields to request downloads from the Android Market. Essentially I’ve added the GetAssetRequest and the GetAssetRequest which was previously missing. I also fixed the unknown field which is known as the isSecure field. This boolean field lets the market know what type of request is coming in and which token will be used.

In the order versions of the vending apk, not everything was send over HTTPS. When most normal requests (get application info, queries, etc) where over unsecured HTTP, the GetAssetsRequest was considered “secure” and only went over HTTPS. This meant the isSecure field had to be set and a new authtoken would be used. Instead of the android service authtoken being sent, the androidsecure token would be sent. This is probably done so that if someone was sniffing requests, they couldn’t just reuse your android token and get away with it, though it may have been done for other reasons.

So what was the mystery of the userId? Well – it was never found on the device, because it was never actually stored on the device. It was only stored sometimes inside of URLS which had been cached or saved to databases. If properly using the protobuf, this relieves the person from needing to find these values, since the market themselves provide them! It was a bit annoying to figure this out finally, but also a huge relief since I wasn’t going crazy not finding the userId anymore.

So let’s sum this all up;

1. Get the android secure token for your account, use this as the authtoken for your next request
2. Build proper request protobuf; Use authtoken from above, set isSecure to true and add the assetId to a GetAssetRequest
3. Receive the GetAssetResponse and build an HTTP request from it using the; blobUrl as the URL and set a cookie using the downloadAuthCookieName/downloadAuthCookieValue pair.

Lastly, I’d like the apologize for the delay in this information. I feel a bit bad since I’ve known this for a while, but most people who ask me questions where just using the hack, which — until very recently worked fine. Also, it has always been possible to just reverse the apk yourself and figure out what was going on ;)

Tim
17 Comments
  1. Could you please provide a sample code?

  2. Hey,
    Thanks for the article it’s really useful.

    I have two questions:
    1- Authtoken you are talking about is the cookie we get for login in or it’s another token google provide ?
    2- The GetAssetRequest needs two variables: the assetId and the directdwonloadkey, where do I get the second one ?
    Thanks for your help

  3. Thanks for the tutorial,
    is this the right protobuf message to send ?
    context {
    authSubToken: “###…”
    isSecure: true
    version: 1002
    androidId: “############”
    deviceAndSdkVersion: “generic:8″
    userLanguage: “en”
    userCountry: “US”
    operatorAlpha: “Android”
    simOperatorAlpha: “Android”
    operatorNumeric: “310260″
    simOperatorNumeric: “310260″
    }
    RequestGroup {
    getAssetRequest {
    assetId: “############”
    }
    }
    I then add the header and make a post request but It fails everytime

  4. Hi,Tim

    Follow you suggest,I still get 400(Bad request),can’t download app right,

    The main step is:

    1.compile the proto file from the your branch
    2.copy it to my project
    3.modify MarketSession.java:
    —–
    if(grp.hasGetAssetResponse())
    val = grp.getGetAssetResponse();
    —–
    public void append(GetAssetRequest requestGroup, Callback responseCallback) {
    request.addRequestGroup(RequestGroup.newBuilder().setGetAssetRequest(requestGroup));
    callbacks.add(responseCallback);
    }
    —————-
    then in my main.java do like this:

    public static void main(String[] args){
    MarketSession session = new MarketSession();
    session.login(“abc@gmail.com”,”abc”);
    session.getContext().setAndroidId(“01234567890″);
    session.getContext().setIsSecure(true).setAuthSubToken(“00571388108397647644″);

    GetAssetRequest assetRequest = GetAssetRequest.newBuilder().setAssetId(“7754377743436207051″)
    .build();

    session.append(assetRequest, new Callback() {
    @Override
    public void onResult(ResponseContext context,GetAssetResponse response) {
    System.out.println(response.toString());
    }
    });
    session.flush();
    }

    but still get 400(Bad request),can you give me more hint?
    btw: setAuthSubToken’s value is get from sniff when start download app from my devices.

    any suggest will be appreciate.
    notenking

  5. @notenking

    Make sure you set an actual valid (and linked to the authtoken) androidId. Also, that is not a valid authtoken you are setting. You need to set the “androidsecure” authtoken.

    @Mike

    Looks good, just make sure your actually setting the proper androidId and the right authtoken (androidsecure).

    @SecureButterFly

    1. The authtoken I’m talking about is a different service than the normal “android” one, it’s the “androidsecure” token. Request that as a service and it should work.
    2. The only required field is the assetId, you can ignore the second.

    @mobi1

    If I have time I’ll commit some more code to the android-market-api. Until then, it should be pretty self explanatory.

  6. There are two auth token needed, one for the request context and the second as a cookie to make the post request, should they be the same or different” ?

  7. @SecureButterfly

    I’m not sure what your asking, let me try to explain again;

    1. To find an assetId like normal — use the “android” authtoken.
    2. After finding the assetId, set the isSecure to be true and set the authtoken to the “androidsecure” auth token. Then create a GetAssetRequest

  8. Sorry I didn’t elaborate enough my question.
    I meant that when sending the request (version=2&request=CATOM…) we specify in the header some information (User-agent, Content-type, cookie: ANDROID=”DAA…” )
    the token inside the header should also be android secure ?

  9. @SecureButterfly

    You actually don’t need to send a cookie as it isn’t verified, until it is specified by the GetAssetResponse.

    Though the ANDROID cookie always maps to the “android” service.

  10. I see, thanks for clarifying :)

  11. finally it works, lots lots of thanks !

  12. Hi Tim,

    I am able to get the android secure authtoken as you suggested.
    But I still couldn’t figure out how do I retrieve the asset id as its different for different session. Please help..

    -Aasha

    • Aasha,

      This ID changes constantly, so you need to perform a search if using the old api before going to the apps page :)

  13. Hi Tim,

    Thanks for getting back to me.
    I am not sure where should I search for the assetID.
    Is there any new api which doesn’t need assetID ?
    Please help me with the same. I am stuck with the issue for very long.

    –Aasha

    • Yes, there is a newer api which no one has bothered reversing and publicly open sourcing.

      You should really goto the Google Group of the project which your asking questions for. Search the group as they’ve _all_ been asked and answered there, then post if you _really_ can’t find the answer.

  14. Thanks a lot Tim..Its working :)

  15. Hi Tim,

    I am new in reversing, now I am working on an project where is used google proto buffers, and curently I use protoc to decode requests … may be there is something else and I cant find it, or may be you can suggest me something …

    thanks ..
    I like your blog, really good thinks ..

    P.S: Sorry for my english, my native language is Russian!

Your Name Email Website