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).

Impersonate a Windows Identity Easily!

Not Me

Why would you want to do Windows Identity impersonation?

Because it’s cool. Really. You can run a program as someone other than who you are. This is especially useful for services, websites, and pretty much anything where you want to run an application in a mode that has different privileges. Do you want more permissions than normal? Use an application in a domain? Lock it down? The sky is the limit!

How is it done?

That part is pretty simple. In fact it’s outlined on this MSDN page. I’ve also got it wrapped up in a much easier to use class here:

using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;
 
namespace ConsoleApplication1
{
    /// Impersonates a windows identity.
    /// Based on: http://msdn.microsoft.com/en-us/library/w070t6ka.aspx
    public class WindowsIdentityImpersonator : IDisposable
    {
        WindowsIdentity _newId;
        SafeTokenHandle _safeTokenHandle;
        WindowsImpersonationContext _impersonatedUser;
 
        public WindowsIdentity Identity { get { return _newId; } }
 
        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        public WindowsIdentityImpersonator(string Domain, string Username, string Password)
        {
            bool returnValue = LogonUser(Username, Domain, Password, 2, 0, out _safeTokenHandle);
 
            if (returnValue == false)
            {
                throw new UnauthorizedAccessException("Could not login as " + Domain + "\\" + Username + ".",
                    new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
            }
        }
 
        public void BeginImpersonate()
        {
            _newId = new WindowsIdentity(_safeTokenHandle.DangerousGetHandle());
            _impersonatedUser = _newId.Impersonate();
        }
 
        public void EndImpersonate()
        {
            if (_newId != null)
            {
                _newId.Dispose();
            }
 
            if (_impersonatedUser != null)
            {
                _impersonatedUser.Dispose();
            }
        }
 
        public void Dispose()
        {
            this.EndImpersonate();
 
            if (_safeTokenHandle != null)
            {
                _safeTokenHandle.Dispose();
            }
        }
 
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
 
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);
    }
 
    public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeTokenHandle() : base(true)
        {
        }
 
        [DllImport("kernel32.dll")]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr handle);
 
        protected override bool ReleaseHandle()
        {
            return CloseHandle(handle);
        }
    }
}

Using this class is simple:

using (var wim = new WindowsIdentityImpersonator("domain", "username", "password"))
{
    wim.BeginImpersonate();
    {
        Console.WriteLine("Thread B: {0}", WindowsIdentity.GetCurrent().Name);
    }
    wim.EndImpersonate();
}

For domain if you want to use your local machine just put in “.”

Ok, that’s cool but what else have you got?

You want more? Okay wise guy, throw this bad boy in a separate thread (even a ThreadPool will work) and you can run that thread under this identity. Want another one? Start another thread. It’s that easy! One application can perform actions under a hundred different identities!

Happy coding!