I got curious the other day if Apple Messages was hackable. In short, yes it is, but not without some headache. You must have a “bridge” to allow you to relay the message, but for people like me who have a mac that is always on this isn’t an issue, here is how the hack works..
Use our bridge.scpt The whole purpose of this applescript is to take the incoming message and send it to perl (you know something useful)
using terms from application "Messages" on message received the_message from the_buddy for the_chat set qMessage to quoted form of the_message set qHandle to handle of the_buddy set qName to first name of the_buddy #do shell script "echo " & qName & "handle " & qHandle & ": " & qMessage & " > /tmp/out.txt" do shell script "echo /usr/bin/perl /Users/john/messages_bridge/messages_bridge.pl " & qName & " " & qHandle & " " & qMessage & ">/tmp/out" do shell script "/usr/bin/perl /Users/john/messages_bridge/messages_bridge.pl " & "'" & qName & "'" & " " & qHandle & " " & qMessage end message received end using terms from |
Here is our very simple perl script.
#!/usr/bin/perl use JSON; use HTTP::Request; use LWP::UserAgent; $key = "pKTjY29WWy"; $name = $ARGV[0]; $handle = $ARGV[1]; $message = $ARGV[2]; $command = "rec"; %message = ("name", $name, "handle", $handle, "message", $message, "key",$key, "command", $command); my $json = encode_json \%message; my $uri = 'http://www.yourdomain.com/chat/api.php'; my $req = HTTP::Request->new('POST', $uri); $req->header( 'Content-Type' => 'application/json' ); $req->content( $json ); my $lwp = LWP::UserAgent->new; $lwp->request( $req ); |
The script converts all the plain-text variables to json for easy sending to the web server, keep in mind there is NO encryption on this, so what is api.php on the webserver?
<?php $connect = mysql_connect("localhost","youruser","yourpassword") or die (mysql_error()); $db = mysql_select_db("imessage",$connect); $postdata = file_get_contents("php://input"); $json = json_decode($postdata); $message = $json->{'message'}; $name = $json->{'name'}; $handle = $json->{'handle'}; $key = $json->{'key'}; $sql = "INSERT into messages (userkey,name,handle,message,rec) values ('$key','$name','$handle','$message',now())"; $result = mysql_query($sql,$connect); ?> |
simply said, it stores all the messages into the messages table just looking to be read!
How can we send a message? Well unfortunately the only way I have figured out is doing polling from the machine running the messages bridge. here is a quick sample.
#!/usr/bin/perl use JSON; use HTTP::Request; use LWP::UserAgent; $key = "pKTjY29WWy"; $command = "get"; %message = ("key",$key, "command", $command); my $json = encode_json \%message; while(1) { my $uri = 'http://www.yourdomain.com/chat/getmessages.php'; my $req = HTTP::Request->new('POST', $uri); $req->header( 'Content-Type' => 'application/json' ); $req->content( $json ); my $lwp = LWP::UserAgent->new; $response = $lwp->request( $req ); if ($response->decoded_content ne "") { my $decoded_json = decode_json($response->decoded_content); print $decoded_json->{'message'}; `osascript send_message.scpt "$decoded_json->{'to_user'}" "$decoded_json->{'message'}"`; } print "Sleeping\n"; sleep 5; } |
on run argv set theHandle to item 1 of argv set theMessage to item 2 of argv tell application "Messages" set theService to first service whose service type is iMessage set theBuddy to first buddy that its service is theService and handle is theHandle send theMessage to theBuddy end tell end run |
The above two scripts the perl script runs preferrably in the background and connects to the outbound queue of the webserver, to get waiting messages, then runs the applescript to actually send them, hacky at best, but sorta works.
What does the getmessages.php file look like? well I am so glad you asked…
<?php $connect = mysql_connect("localhost","username","password") or die (mysql_error()); $db = mysql_select_db("imessage",$connect); $postdata = file_get_contents("php://input"); $json = json_decode($postdata); $key = $json->{'key'}; $sql = "SELECT * from outbound where userkey=\"$key\" and seen=0"; $result = mysql_query($sql,$connect); while ($row = mysql_fetch_array($result)) { $id = $row[4]; error_log($id); $line = array("to_user"=>$row[1],"message"=>$row[2]); echo json_encode($line); $sql = "UPDATE outbound set seen = 1 where id=\"$id\""; $r = mysql_query($sql,$connect); } ?> |
there you go, now you have all the scripts required to send/receive messages on a non Apple device, but how can you make imessage function??
You MUST setup Messages to run an applescript when a message comes into you.
Go to the Alerts Tab.
Use the built in apple script to automatically accept new messages.
I built a quick sample. If you read the source code above you see a key, this key is used when your Apple Messages sends to your webserver. Here is my sample code and a few pictures.
index.php
<html> <head> <title>iMessage Chat!</title> </head> <body> <form method="get" action="generate.php"> <table border="0"> <tr> <td><input type="submit" value="Generate message ID"></td> </tr> </table> </form> <hr /> <form method="get" action="chat.php"> <table border="0"> <tr> <td>Message ID:</td><td><input type="text" name="id"></td> </tr> <tr> <td colspan="2"><input type="submit" value="Chat Now!"></td> </tr> </table> </form> </form> </body> </html> |
generate.php used for generating the user key, probably not the most secure, but “works”
<?php $connect = mysql_connect("localhost","username","password") or die (mysql_error()); $db = mysql_select_db("imessage",$connect); $found = true; $string = ""; while ($found) { $string = generateRandomString(); $sql = "SELECT count(*) from users where user_key=\"$string\""; $result = mysql_query($sql,$connect); $row = mysql_fetch_row($result); if ($row[0] == 0) { $found = false; } } $sql = "INSERT into users (user_key) values (\"$string\")"; $result = mysql_query($sql,$connect); echo "Your Key is: $string"; function generateRandomString($length = 10) { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, strlen($characters) - 1)]; } return $randomString; } ?> |
chat.php for the conversation (one at a time convo)
<html> <head> <title>Chatting</title> <script type="text/javascript"> var last_epoch = 0; </script> <script type="text/javascript" src="jquery-1.10.2.min.js"></script> <script type="text/javascript"> function callAjax() { $.get('checkmessages.php?key=<?php echo $id?>&last_check='+last_epoch, function( data ) { $( ".chat" ).append( data ); }); last_epoch = new Date() / 1000; } setInterval(callAjax,2000); callAjax(); function sendMessage() { var message =document.getElementById('message').value; $.get('sendmessage.php?key=<?php echo $id?>&message='+message, function( data ) { $( ".chat" ).append( "You: " + message +"<br />"); }); document.getElementById('message').value=""; } </script> </head> <body> <div style="margin:0 auto; width:800px; height:95%; overflow:hidden"> <div class="chat" id="chat" style="border: 1px solid red; width:800px; position:absolute; bottom:50px; top:0px; overflow:auto"> </div> </div> <table border="0" width="100%"> <tr> <td width="10">Message</td> <td><input type=text" id="message" name="message" size=100> <input type="button" value="Send" onClick="sendMessage()"></td> </tr> </table> </body> </html> |
checkmessages.php actually checks for new messages.
<?php $start = $_GET['last_check']; $key = $_GET['key']; $ts = time($start); $start = date("Y-m-d H:i:s"); error_log($start); $connect = mysql_connect("localhost","username","password") or die (mysql_error()); $db = mysql_select_db("imessage",$connect); $sql = "SELECT * from messages where userkey=\"$key\" and seen = 0"; $result = mysql_query($sql,$connect); while ($row = mysql_fetch_array($result)) { $name = $row[1]; $handle = $row[2]; $message = $row[3]; $id = $row[6]; if (strstr($name,"missing value")) { echo "$handle: $message<br />\n"; } else { echo "$name ($handle): $message<br />\n"; } error_log("$name ($handle): $message"); $sql = "UPDATE messages set seen=1 where id=\"$id\""; $r = mysql_query($sql,$connect); } ?> |
Here is a sample of how it looks in firefox:
I had a grandious vision of creating an Messages to android bridge, but alas winter is coming, and that means beer brewing time, and less time to code! Let me know if you came up with anything with the help or inspiration of the above code!!
John
“Hack the planet”