Update: I have received an email requesting the use of this quote as to the one I had previous:
Thank you Izaak, this is brilliant work. Prior to coming here I had no experience in programming, servers, or the tools used here as outlined in your method. I know you get a lot of requests to (and it would have saved time if you) ‘spoon feed’ the answer but I understand that position is not one you want to be in for legal reasons. What you have outlined in these 2 posts has been deeply entertaining, educational AND effective. It does work and anyone who follows the instructions and doesn’t get their answer should go back and read it again. The answers are all here. For a tool (Apache) I didn’t understand how to set up I simply Googled it – there was an instructional video on youtube. Izaak you’ve already been very generous with your time and I look forward to your next projects. To everyone else – this isn’t easy stuff but if you do the work to learn you might be rewarded in more ways than simply expanding your mind.
At that request, I have kindly made it slightly more challenging to achieve this article’s goals.
Update: Many people have asked if this code still works, and it does. In fact, a pure HTML/Javascript implementation is possible.
Note: If you haven’t read the previous post related to this subject, you need to do so before you can make sense of any of this. This article is long and complicated and not for the faint of heart. It is, however, terribly fun!

So I was minding my own business, as usual, until I received this email:
Dear Mr Schroeder!
My name is Tom Shao from Canada. I was excited that you can reverse Engineering Java and obtain the secret password. Your instruction in the weblog is very detailed. However I don’t have eclipse installed on my desktop. I just wonder if you can do me a favor to help me get my password. My information is as follows:
[Omitted for obvious privacy reasons]
Any help would be much appreciated.
Thanks,
Tom
Now normally I don’t go off and just do work for people asking for stuff, given it’s supposed to be a learning experience; not a “hurr hurr give me free stuff” experience. However, only a few hours later I receive ANOTHER email!
I downloaded Eclipse and tried to debug it with no lock [sic] as the class KC is different with yours. I assume they updated the new version and fixed the problem.
Can you send me your Freephoneline software? I am desperate to need your help?
Thanks,
Tom
So you at least get some points for trying. As it turns out, they did indeed update their software, but it certainly wasn’t to “fix” that problem; in fact if anything they turned their program from a reasonable softphone client to a horrendously ugly (like I’ve taken shits that look better kind of ugly), slow, ad-filled piece of garbage.

The reason you can’t exactly follow my previous post is that the obfuscation process randomizes class names, methods and so on; every time they release a new version you will end up with a different naming scheme. The process of finding your credentials works the exact same way in the new version, introducing a manual break in the program and hunting through the class variables until you find the password.
But all is not lost for you Tom! I will help you redeem yourself! I have provided a handy comparison chart for the rest of our readers:

Thanks to you Tom, (but mostly me), other people now too can have free calls with little effort! This blog post covers something a bit more interesting and useful than what was discussed previously. It deals with discovering the FreePhoneLine authentication protocol itself and how to emulate it; it also exposes a possible security hole in that particular protocol.

Shall we find out?
Let’s start with what we know. You provide your FreePhoneLine username and password, which gets sent to the server and they send you your SIP username and password back; all of this is encrypted and therefore one can’t simply WireShark the information off the network.
We want to be able to achieve two things:
- Be able to construct the appropriate request to get our encrypted username/password
- Be able to decrypt the response so that we may use our SIP username/password
We’ll start things off the same way we did the previous article, breaking the program and finding out which class holds the clear-text password, and from there which class actually decrypts the password to its clear-text form. I’m not going to bore you with old hat; setup the project the same way as before and use the same techniques achieve the following result:

We’ve discovered that the class called “f” contains the connection information; particularly of interest are fields “e” and “g” which hold the SIP username and password respectively. We’re interested in finding out what modifies those variables; since they are public variables, (denoted by the green circle), they can be modified by any class; so we can use a feature in Eclipse very similar to a breakpoint called a watchpoint!
Watchpoints monitor variables; they will pause the execution of the program when a variable is read from, written to or both. Those familiar with x86 debuggers will note a similar feat can be achieved with hardware breakpoints. To set a watchpoint, find the class in the package viewer and select “Toggle watchpoint”.

Close the program if it’s being run and start debugging it again. The program will suspend immediately and drop you back into the debugger. What’s happened? The password isn’t being loaded in so soon obviously, as we have not even logged in yet. The answer is disappointingly simple; the variable is simply being initialized to null, (i.e. the program is writing null into the variable). We can just resume the program. But wait! It pauses again! What’s going on this time? Again, the answer is disappointingly simple. The variable is being set to the empty string “”. Remember! Null is not the same as the empty string! The empty string is still a string object, the null string is nothing. You can see this by hitting the “Step over” button and you can watch “g” change from null to the empty string.

Anyway, we can go back to running the program and you should be able to hit up the login box and enter your credentials before the program breaks you back into the debugger again. You should then end up with something like the following:

This looks promising! You’ll note if you look in the variables view that the field “g” still does not have your password. That means if we hit “step over” your password will suddenly appear! That’s because something within “wo.c” (remember, “c” is a method of the class “wo”) is saying “g = SIPPassword”. That’s the whole purpose of a watchpoint. So we basically want to understand what goes on inside “wo.c” that is setting our password.

But how on Earth do we begin to do that when we don’t have any source code to work with? Well, we just make source code! Using a tool called a decompiler we can turn a Java “.class” file back into a “.java” file! Pretty impressive! Not all decompilers were created equal, and a lot of semantic information is lost in the decompilation process, but the result is still extremely useful!
I have chosen to use a Java decompiler called JD. (Original name, eh?) You can get your copy here. Extract “JD-GUI”, and run it. Open the “wo.class” file with it.

And then find the “c(ActionEvent)” method within it. There’s a lot of code in that class, so I won’t bother pasting it all. I will, however, highlight a few parts that tell us we’re probably going in the right direction!
String str1 = this.a.getText();
String str2 = String.valueOf(this.i.getPassword());
See that “getPassword()” there? That’s probably getting the password you entered, and the “getText()” above it is probably fetching your username! And as we scroll down that code chunk:
this.l.e = zc.d();
this.l.g = dc.a(zc.f(), this.l.e); //THIS LINE TRIGGERED OUR WATCHPOINT! l.g IS ASSIGNED HERE!
setVisible(false);
And once you see it visually the correlation becomes even more obvious:

We can make an educated guess that “zc.d” fetches the username, “zc.f” fetches the encrypted password and “dc.a” handles the decryption! And once everything is done the login windows calls “setVisible(false)” to hide itself from view. Things are coming together nicely! In JD simply click on the underlined “dc” class and we can start checking out the “dc.a” function.
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
When you see things like that at the top the file, you just know you’re in the right spot; they’re using standard ol’ Java cryptography APIs! That will make things even easier on us because all those APIs are very well documented. We can check out the rest of the file and we realize that there are a few functions called “a”. The one that’s being called takes two arguments.
this.l.g = dc.a( //returns a string because l.g is a string, obvious from the debugger
zc.f(), //we don't know this type; we could check the return type of zc.f though
this.l.e //this is a string type, obvious from the debugger
);
But since this function, “dc.a” is called from “wo.c”, we know that whatever particular “a” is being called also needs to be a public method! This narrows it down to just one function:
public static String a(byte[] paramArrayOfByte, String paramString)
Which we observe basically only does one important thing:
byte[] arrayOfByte = a(false, paramArrayOfByte, paramString);
return new String(arrayOfByte).trim();
It calls ANOTHER a and returns its value in string form! That particular “a” is easily identifiable as being the only one which takes a boolean argument initially.
private static byte[] a(boolean paramBoolean, byte[] paramArrayOfByte, String paramString)
{
int i = cc.c;
byte[] arrayOfByte1 = a(paramString);
SecretKeySpec localSecretKeySpec = new SecretKeySpec(arrayOfByte1, z[1]);
Cipher localCipher = Cipher.getInstance(z[2]);
IvParameterSpec localIvParameterSpec = new IvParameterSpec(z[0].getBytes());
//...
}
Is obviously the most interesting one; it actually makes use of cryptographic functions! A quick trip to their API documentation pages reveals the purpose of their arguments.
Now we know:
private static byte[] a(boolean paramBoolean, byte[] paramArrayOfByte, String paramString)
{
int i = cc.c;
byte[] arrayOfByte1 = a(paramString);
//First argument is the decryption/encryption key, second argument is the algorithm being used
SecretKeySpec localSecretKeySpec = new SecretKeySpec(arrayOfByte1, z[1]);
//Only argument is the algorithm being used
Cipher localCipher = Cipher.getInstance(z[2]);
//Only argument is the data for the initialization vector
IvParameterSpec localIvParameterSpec = new IvParameterSpec(z[0].getBytes());
//...
}
We are, however, lacking all values of “z”; these values are important as they’ll clearly tell us both the algorithm used for decryption and the initialization vector. They are not present in the decompiled code, so we’ll have to get them from the debugger. We also note “z” is a static class variable, and so we should be able to pick out said values any time. Given we’re interested how the decryption function works, we should breakpoint that and then we can start to kill two birds with one stone!

We can also now disable the watchpoint on “f.g” as we’ve found the method of interest. Restart the process and wait till you hit the breakpoint from logging in. (You’ll hit the breakpoint once when the program starts, but I have yet to figure out why).

Expressions let us inspect arbitrary parts of the program. It’s time to add one so we can find out the value of that mysterious “z”! Right click in the expressions pane and select “Add Watch Expression…”

And then enter the value “dc.z” as that is the variable we are interested in.

And then watch the magic!

This tells us some very important things! They’re just using plain old AES to encrypt their data and what the initialization vector is they use to encrypt it! So that’s one bird. Now for the other; we want to see how the function is actually used and what values are fed into it. If you go back to the variables tab you’ll see what the arguments to the function are. But things are not as they seem…

What on earth is the web username doing there?
this.l.g = dc.a(
zc.f(), //we assume this returns encrypted data
this.l.e //this is the SIP username, NOT the web username!! So what gives?
);
Well clearly “dc.a” is being called from somewhere other than just that line above or we’ve really messed things up; but to be safe we’ll hit “Continue” in the debugger and see if we can reach a point where “arg2″ is the value we expect, the SIP username (i.e. phone number).

Our patience is rewarded! Let’s go back and see how this plays out in our function.
private static byte[] a(boolean paramBoolean, byte[] paramArrayOfByte, String paramString)
{
int i = cc.c;
//So this basically calls a("16042834338") and sets arrayOfByte1 to its value
byte[] arrayOfByte1 = a(paramString);
//This creates a new key using the output of the previous function for an "AES" cipher
SecretKeySpec localSecretKeySpec = new SecretKeySpec(arrayOfByte1, z[1]);
//Creates an "AES/CBC/NoPadding" cipher instance
Cipher localCipher = Cipher.getInstance(z[2]);
//Creates an initialization vector using the string "fedcba9876543210"
IvParameterSpec localIvParameterSpec = new IvParameterSpec(z[0].getBytes());
The next part appears dependent on the boolean parameter. We need to know a little more about the “Cipher” object before we can determine what this function does. A quick trip to the API reference shows the first parameter determines the ciphers operating mode (encryption vs decryption for example), with the other arguments being self explanatory: the encryption key and the initialization vector. “arrayOfByte2″ appears to be the data which is either encrypted or decrypted.
byte[] arrayOfByte2;
if (i == 0)
{
if (paramBoolean) //false in our case
{
//initialize to encryption mode
localCipher.init(1, localSecretKeySpec, localIvParameterSpec);
//set the data to be encrypted via "md.a" using the third parameter of this function
arrayOfByte2 = md.a(paramArrayOfByte);
if (i == 0);
}
else //so we go here
{
//initialize to decryption mode
localCipher.init(2, localSecretKeySpec, localIvParameterSpec);
}
}
else
//data to be processed is just the input
arrayOfByte2 = paramArrayOfByte;
//perform the encryption/decryption of arrayOfByte2
byte[] arrayOfByte3 = localCipher.doFinal(arrayOfByte2);
//return the processed data
return arrayOfByte3;
}
The “i” variable is a little confusing and it may be a side-affect of the decompiler not doing something right; from here on out we have to start making some educated guesses. We know the processing key is ALWAYS altered by some function, (ironically ALSO called “a”), we know one function does both encryption and decryption, we know that when the SIP username is involved as part of the key argument this function operates in decryption mode. It is likely safe to assume that the second parameter is therefore the encrypted SIP password.
I’m going to briefly force you to go back in time, to when the web username was present instead of the SIP username. Look at the first parameter; it’s true instead of false! This means that your web username is being used to encrypt something! This might be useful knowledge for later!
We’re getting really close now! We basically need to understand what the key modification function does.
byte[] arrayOfByte1 = a(paramString);
You will find its basic purpose is thus:
ec localec = new ec(paramString);
byte[] arrayOfByte = md.b(localec.a().substring(0, 16));
return arrayOfByte;
We’re now involving another TWO new classes! Egads! So we need to understand “ec” as well as the function “md.b”. Let’s deal with “ec” first by opening it up in the decompiler. The constructor is nothing interesting, it just saves the given key to a local variable. We’re interested in the “ec.a” function, which does nothing really but call “ec.b”. So let’s look at that.
It seems to involve message digests and another missing variable!
localMessageDigest = MessageDigest.getInstance(z[1]);
Not even looking I imagine just like how “AES” was selected as the crypto algorithm, “z[1]” represents the choice of message digest algorithm. What is “z[1]“? Remember how we found “dc.z”? You can use exactly the same trick to find “ec.z”. Add a method breakpoint to “b”, add a watch expression for “ec.z”, disable all your other breakpoints and restart the debugger. You’ll have your z in no time!

So we now know that MD5 is somehow involved in transforming the given crypto key. Not like MD5 and AES has ever been used before…

arrayOfByte = localMessageDigest.digest(this.a.getBytes());
Seems to perform the actual MD5 on the crypto key. We then observe what happens to the variable “arrayOfByte”. We see a loop and observe a lot of “Integer.toHexString(arrayOfByte[i]);” calls. The educated guess says that the returned MD5 hash is raw binary data and they are turning it into its hex form. However, we should attempt to verify this. We note at the very end:
return this.b;
They use b to store the result of the transform. We can simply add a watchpoint to “ec.b” and we’ll be able to test!

We know “ec.a” stores the plaintext value, so we just disable all other breakpoints, restart the program and compare! Remember, when the breakpoint hits, use “Step Over” so the value actually gets assigned. The first breakpoint you hit will likely be “b” being initialized to “null”, but the second… well that should be just magical.

And now we just check to make sure our hypothesis is correct:
echo -n "16042834338" | md5sum
b904785204cd684ff3965bc263220fc6
And it is! Fantastic! We now know “ec” is basically an MD5 hashing class. We go back to the line of code dealing with the transform and note that it appears to take only the resultant first 16 characters of the hex form of the MD5 hash and then pass those 16 characters into “md.b”.
md.b(localec.a().substring(0, 16));
So once again open up the decompiler and find the “md.b” that takes a single string as its argument. It seems to involve a bunch of checks, but its main purpose seems to revolve around the line:
arrayOfByte[i] = (byte)paramString.charAt(i);
It essentially seems to just convert a string into an array of bytes. Why they weren’t using String.toBytes() who knows. But to say the least that function doesn’t seem to bastardize anything about the crypto key.
With all this information it is hard to think that we are not done yet. We still don’t know how to get the encrypted password and we still need to test our ability to decrypt that password. We know the encrypted password is sent to us over the air, so we can at least start there. Disable all your breakpoints, restart the program and fire up WireShark.
NOTE: For your own good, turn off your torrents, streaming music and other things which obsessively fire packets off over your network. It will make your life MUCH easier trying to sift through things in WireShark.

With as little delay as possible start capturing packets and log into FreePhoneLine. As soon as you see yourself log in stop capturing packets and start to browse through the dump looking for things related to FreePhoneLine. One quickly finds that the authentication happens over HTTP and that the typical response looks something like:

One can make an educated guess that the encrypted password is base-64 encoded, simply by seeing the “==” on the end and knowing that encrypted data almost always falls outside the printable ASCII range.
So let’s build a program to decrypt these passwords! I’m going to use (yea haters gonna hate) PHP because it comes with cryptography functions built in and is fast to prototype things with.
<?php
function new_freephoneline_crypto_context($Key) {
$Key = substr(md5($Key), 0, 16); //The first 16 characters of the hex represntation of the MD5 of the key
$IV = 'fedcba9876543210'; //The IV they use
if ($CryptoModule = mcrypt_module_open('rijndael-128', '', 'cbc', '')) { //Open the cipher (AES _is_ Rijindael)
mcrypt_generic_init($CryptoModule, $Key, $IV); //Initialize
return $CryptoModule; //Return
} else {
die('Initializing mcrypt failed!');
}
}
function delete_freephoneline_crypto_context($CryptoModule) {
mcrypt_generic_deinit($CryptoModule);
mcrypt_module_close($CryptoModule);
}
$SIPUsername = '16042834338';
$EncryptedSIPPassword = base64_decode('WhubSHQz2nqFNK/N0xGMvw==');
$Context = new_freephoneline_crypto_context($SIPUsername);
$DecryptedPassword = mdecrypt_generic($Context, $EncryptedSIPPassword);
delete_freephoneline_crypto_context($Context);
$Output = 'Username is: '.$SIPUsername.', and password is: '.$DecryptedPassword;
echo $Output;
?>
Funny thing is, it works. You can now successfully take any of their encrypted SIP passwords and derive their plaintext ones. But we’re not done yet. So far we still need to use WireShark to get at those encrypted passwords. We want a truly automated process. We need to know how FreePhoneLine requests that information as well. You can easily find it in WireShark.

Le sigh. It looks like they encrypt the web password going out as well, and we’re going to have to mimic that. But wait… don’t you remember that one time we saw the debugger stop and use our web username as a key for ENCRYPTION? It’s starting to come together now isn’t it? We can make another educated guess in that it simply encrypts our web password with our web username in the exact same manner it decrypts the SIP one. We can write another program to test this!
This is also a bit of a crapshoot, as I have no idea how “key” is generated, or even if its necessary. But after some testing it turned out that not providing “key” doesn’t make any difference.
<?php
$WebUsername = 'izaak.schroeder@gmail.com';
$WebPassword = 'lolfreephoneline';
$Context = new_freephoneline_crypto_context($WebUsername);
$EncryptedWebPassword = mcrypt_generic($Context, $WebPassword);
delete_freephoneline_crypto_context($Context);
if ($CURLHandle = curl_init('http://www.freephoneline.ca/services/init')) {
curl_setopt($CURLHandle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($CURLHandle, CURLOPT_HEADER, false);
curl_setopt($CURLHandle, CURLOPT_POST, true);
curl_setopt($CURLHandle, CURLOPT_POSTFIELDS, ''
.'web_username='.urlencode($WebUsername).'&'
.'web_password='.urlencode(base64_encode($EncryptedWebPassword)).'&'
.'key='
);
$Data = array();
foreach(explode("\r\n", curl_exec($CURLHandle)) as $Line)
if (false !== $Target = strpos($Line, '='))
$Data[trim(substr($Line,0,$Target))] = trim(substr($Line, $Target+1));
curl_close($CURLHandle);
echo 'SIP username: '.$Data['sip_username'].', encrypted SIP password: '.$Data['sip_password'];
} else {
die('Initializing CURL failed!');
}
?>
Oh look, it works! We have effectively and nearly completely (save for whatever “key” does) emulated the FreePhoneLine authentication protocol. Combining those two scripts might leave you with something like:
<?php
//This file is called ffd.php
$Output = 'Nothing interesting to show here Capt\'n';
function new_freephoneline_crypto_context($Key) {
$Key = substr(md5($Key), 0, 16);
$IV = 'fedcba9876543210';
if ($CryptoModule = mcrypt_module_open('rijndael-128', '', 'cbc', '')) {
mcrypt_generic_init($CryptoModule, $Key, $IV);
return $CryptoModule;
} else {
die('Initializing mcrypt failed!');
}
}
function delete_freephoneline_crypto_context($CryptoModule) {
mcrypt_generic_deinit($CryptoModule);
mcrypt_module_close($CryptoModule);
}
if (isset($_REQUEST['u']) && isset($_REQUEST['p'])) {
$WebUsername = $_REQUEST['u']; $WebPassword = $_REQUEST['p'];
$Context = new_freephoneline_crypto_context($WebUsername);
$EncryptedWebPassword = mcrypt_generic($Context, $WebPassword);
delete_freephoneline_crypto_context($Context);
if ($CURLHandle = curl_init('http://www.freephoneline.ca/services/init')) {
curl_setopt($CURLHandle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($CURLHandle, CURLOPT_HEADER, false);
curl_setopt($CURLHandle, CURLOPT_POST, true);
curl_setopt($CURLHandle, CURLOPT_POSTFIELDS, ''
.'web_username='.urlencode($WebUsername).'&'
.'web_password='.urlencode(base64_encode($EncryptedWebPassword)).'&'
.'key='
);
$Data = array();
foreach(explode("\r\n", curl_exec($CURLHandle)) as $Line)
if (false !== $Target = strpos($Line, '='))
$Data[trim(substr($Line,0,$Target))] = trim(substr($Line, $Target+1));
curl_close($CURLHandle);
if (!isset($Data['sip_username']) || !isset($Data['sip_password'])) {
$Output = 'Error getting credentials; perhaps wrong username or password?)';
} else {
$SIPUsername = $Data['sip_username'];
$EncryptedSIPPassword = base64_decode($Data['sip_password']);
$Context = new_freephoneline_crypto_context($SIPUsername);
$DecryptedPassword = mdecrypt_generic($Context, $EncryptedSIPPassword);
delete_freephoneline_crypto_context($Context);
$Output = 'Username is: '.$SIPUsername.', and password is: '.$DecryptedPassword;
}
} else {
die('Initializing CURL failed!');
}
}
?><html>
<head>
<title>Freephoneline SIP Credentials Decryptor</title>
</head>
<body>
<form method="post" action="ffd.php">
<fieldset>
<legend>FreePhoneLine Login Information</legend>
<div>
<label>Username:</label>
<input style="width: 200px;" type="text" name="u" placeholder="username" value="<?php echo isset($_REQUEST['u']) ? $_REQUEST['u'] : '' ; ?>"/>
</div>
<div>
<label>Password:</label>
<input style="width: 200px;" type="password" name="p" placeholder="password" value="<?php echo isset($_REQUEST['p']) ? $_REQUEST['p'] : ''; ?>"/>
</div>
</div>
<div><input type="submit" value="Get My SIP Info"/></div>
</form>
<hr/> <?php echo $Output; ?>
</body>
</html>
After such a long journey seeing something like:

is immensely satisfying, isn’t it Tom?
And what about that security hole I mentioned? Well thing is, they encrypt your web password with your web username… both of which are sent in plaintext over the wire. You can already see how dumb of an idea this is. If someone is using FreePhoneLine on your network and you have WireShark going and you get their encrypted web password and their login… guess what! You have their decrypted web password too!
Anyway, hope you’ve enjoyed this little adventure. And remember kids, only you can prevent bad designs from being used!
Until next time!