Find an iPhone with C#

FindMyWife

 

This has happened to me a few times, has it happened to you? My wife asks, nay orders me, to call her at a certain time and invariably out of politeness has switched her phone to vibrate and cannot hear me call no matter how many times I try. So she has the really nice iPhone 4 and I have whatever my place of employment has given me (not iPhone or Android) so my options are limited if I don’t have a computer. Well, you can say goodbye to that!

With the help of Fiddler I was able to track down the API calls that iCloud makes to ping a phone, so now I can make a simple web page that will ping her phone and all I need is my Apple ID, Password, and the name of the Phone / iMac / iPad / iPod / iGiveUp. It’s even in a nice static class for you so you can use it anywhere. Enjoy!

 

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Script.Serialization;
 
namespace ConsoleApplication1
{
  public static class iCloud
  {
    const string iCloudUrl = "https://www.icloud.com";
    const string iCloudLoginUrl = "https://setup.icloud.com/setup/ws/1/login";
    const string iCloudPlaySoundUrl = "https://p03-fmipweb.icloud.com/fmipservice/client/web/playSound";
    const string iCloudInitClientUrl = "https://p03-fmipweb.icloud.com/fmipservice/client/web/initClient";
 
    public static void Ping(string appleId, string password, string deviceName)
    {
      WebClient wc = new WebClient();
      string authCookies = string.Empty;
 
      wc.Headers.Add("Origin", iCloudUrl);
      wc.Headers.Add("Content-Type", "text/plain");
      wc.PostDataToWebsite(iCloudLoginUrl, string.Format(
        "{{\"apple_id\":\"{0}\",\"password\":\"{1}\",\"extended_login\":false}}",
        appleId, password));
 
      if (wc.ResponseHeaders.AllKeys.Any(k => k == "Set-Cookie"))
      {
        wc.Headers.Add("Cookie", wc.ResponseHeaders["Set-Cookie"]);
      }
      else
      {
        throw new System.Security.SecurityException("Invalid username / password");
      }
 
      var jsonString = wc.PostDataToWebsite(iCloudInitClientUrl,
        "{\"clientContext\":{\"appName\":\"iCloud Find (Web)\",\"appVersion\":\"2.0\"," +
        "\"timezone\":\"US/Eastern\",\"inactiveTime\":2255,\"apiVersion\":\"3.0\",\"webStats\":\"0:15\"}}");
 
      if (jsonString.StartsWith("{\"statusCode\":\"200\""))
      {
        var js = new JavaScriptSerializer();
        var response = js.Deserialize(jsonString, typeof(object)) as dynamic;
        var content = response["content"];
        foreach (Dictionary<string,object> o in content)
        {
          if (o.Values.Contains(deviceName))
          {
            var psResult = wc.PostDataToWebsite(iCloudPlaySoundUrl, string.Format(
              "{{\"device\":\"{0}\",\"subject\":\"Find My iPhone Alert\"}}", o["id"]));
 
            return;
          }
        }
      }
    }
  }
}

You will also need my extension method for WebClient.PostDataToWebsite here:

public static class Extensions
{
  public static string PostDataToWebsite(this WebClient wc, string url, string postData)
  {
    var result = string.Empty;
 
    wc.Encoding = System.Text.Encoding.UTF8;
    wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
 
    result = wc.UploadString(url, "POST", postData);
 
    return result;
  }
}

And the brave among you will be able to try it out via this secure page: iCloud Pinger (SSL Encrypted & I don’t log your credentials).

4 thoughts on “Find an iPhone with C#”

  1. Great code! A couple of things one line

    if (wc.ResponseHeaders.AllKeys.Any(k => k == "Set-Cookie"))

    > should be a greater than symbol.

    That aside, I got your code to compile, and I was brave enough to try your online version and neither are working. Has Apple changed this API? Do you have plans to fix this? I’d be interested in helping if you are interested.

    1. Yeah, I noticed since iOS 7 it stopped working, so there must have been a change. Recently I’ve had a lot of projects so this blog is on the back-burner but if you get it to work, I’ll post your code! Otherwise, it might be awhile… or at least until I can’t contact my wife again. :-)

      1. By changing the URL as below,

        For iOS7: –
        const string iCloudPlaySoundUrl = “https://p19-fmipweb.icloud.com/fmipservice/client/web/playSound”;
        const string iCloudInitClientUrl = “https://p19-fmipweb.icloud.com/fmipservice/client/web/initClient”;

        For iOS6: –
        const string iCloudPlaySoundUrl = “https://p03-fmipweb.icloud.com/fmipservice/client/web/playSound”;
        const string iCloudInitClientUrl = “https://p03-fmipweb.icloud.com/fmipservice/client/web/initClient”;

        Still investigating for iOS8.

        Let me know you have any solution on iOS8.

        Regards,
        Manoj

  2. This is not actually an iOS 7 problem. I could find my iPhone running iOS 7 using this code, but trying to find a phone on a different account, also running iOS 7, did not work. I did some packet level analysis of requests from the actual iCloud website, and found the problem to be the iCloudPlaySoundUrl and iCloudInitClientUrl. It appears each iTunes account is paired with one of several servers to perform the fmipservice lookup, and you must run the query against the server your device is paired with.

    After logging into iCloud itself, a JSON document is returned which contains the server for findme lookups. I used the JSON.Net library to parse this out, shortened the Urls to not include the server parameter, and then prepend the server returned by iCloud to this value:

    const string iCloudPlaySoundUrl = “/fmipservice/client/web/playSound?dsid=”;
    const string iCloudInitClientUrl = “/fmipservice/client/web/initClient?dsid=”;
    (…)
    string data = wc.PostDataToWebsite(iCloudLoginUrl, string.Format(
    “{{\”apple_id\”:\”{0}\”,\”password\”:\”{1}\”,\”extended_login\”:false}}”,
    appleId, password));

    JObject obj = JObject.Parse(data);
    string iCloudDeviceUrl = (string)obj[“webservices”][“findme”][“url”];
    var jsonString = wc.PostDataToWebsite(iCloudDeviceUrl + iCloudInitClientUrl,
    “{\”clientContext\”:{\”appName\”:\”iCloud Find (Web)\”,\”appVersion\”:\”2.0\”,” + (…)

    var psResult = wc.PostDataToWebsite(iCloudDeviceUrl + iCloudPlaySoundUrl, string.Format(
    “{{\”device\”:\”{0}\”,\”subject\”:\”Find My iPhone Alert\”}}”, o[“id”]));

    It should then work correctly.

Leave a Reply