Exploring IOTA #ICT-2, Reverse engineering the code Part-1

We explore how to decompile java source code with the help of publicly available web-based java decompilers. Then recompile it using the standard java compiler. Changing the code in order to confirm receiving UDP packets. With that starting to understand the code of ICT hopefully 😄

Jan Bertrand
Coinmonks
Published in
7 min readJul 24, 2018

--

Maybe that’s a bit too complex for me, trying to understand just by looking at the code the underlying purpose of IOTA controlled agent (ICT). Major reasons:

  • I am not a coder by profession
  • I have no experience with swarm logics
  • my first experience with Java dates back to school-time but that was in the early 90s.

Anyway — lets just do it.

I am still testing this on my google cloud instance (f1-micro). My text editor is vim (which i have kind of hate/love relationship already) — please have look at my other articles if you want to set this environment up yourself.

This article is not intended to harm or disturb the testing phase of ICT. It documents my trial to understand the underlying mechanics of ICT by looking at the code base (there is not much else at the moment except that — no documentation). I’ve asked Come-from-Beyond on discord #ICT channel to publish this baby steps.

Furthermore I might be totally wrong with my findings 😄

Decompiling the class files

If you were one of the lucky ones able to download the first pre-alpha release from ICT (0.1.1) (in the official IOTA discord channel) you can follow up on all the next steps. If not then maybe there will be the next chance on version 0.1.2. Come-from-Beyond mentioned on discord that he is releasing the code after refactoring anyway soon.

The *.class files (compiled *.java code) are compressed hidden in that little zip file. After you extract the files you could use available online decompliers which try to inverse the compilation by using different strategies. I used this site

http://www.javadecompilers.com/

Those different strategies can be selected upon uploading the class file. I tried CFR first but wasn’t successful — the code was not entirely readable and more importantly it wasn’t able to be compiled again. The best result I achieved with the Procycon strategy.

  • decompile all class files in the zip folder with Procycon and store the resulting java files.

We can use any text editor for having a first glance at the code.

There are five files to be looked at

  • Ict.java — the main class file which hold the complete schedule and main function.
  • Transaction.java — the class which analysis the received IOTA transactions and returns errors if not valid
  • Converter.java — which translates the received transactions from bytes to trits and trytes (those conversion transfer functions are string based)
  • Curl.java — the infamous hash function recalculating the hash of this transaction and confirm the validity of the so called nonce.
  • Neighbor.java — just logging incoming/outgoing transactions into groups (all, new, invalid, requested, shared)

Recompiling the java files

I mean I had no clue about the code after decompiled the class files. But Iknew I need to somehow check how i can compile those java files again to finally start the application again.

First thing you need to have the command available to compile the classes. Which means you might need to change from the Java Runtime Environment JRE to the Development Kid JDK, which just needs a couple of 50MB more than the former.

~$ sudo apt-get update
~$ sudo apt-get install default-jdk

Next we are going to compile all java files and move them into the dedicated class path indicated in the java files. Lets assume we have created a subfolder IOTA

~/IOTA$ javac *.java
~/IOTA$ mkdir cfb && cd cfb
~/IOTA/cfb$ mkdir ict
~/IOTA$ mv *.class cfb/ict/

The class file need to be placed in a relative path to where you start the application or we would need to add their location in environment PATH variables. Looking at the first lines of our decompiled Ict.java file shows that java expect the package and classes to be found in cfb.ict.

// 
// Decompiled by Procyon v0.5.30
//

package cfb.ict;

import cfb.ict.Converter;
import cfb.ict.Neighbor;
import cfb.ict.Transaction;

import java.util.HashMap;
import java.net.SocketAddress;

Other classes like HashMap and SocketAddress are imported from the known libraries within java.

With this we can start the recompiled java program. We just need to place a file ict.properties (with only one line port=11111) in our IOTA folder. From here we can start the program with the following command

🔥 Please don’t use the recompiled ICT with real IRI neighbors on the main-net. This is why i have used a different port (11111). We don’t know whether the program is working properly and we don’t want to disturb the tests. Furthermore if your neighbors have added you, they did most likely because they expect you to have a normal ICT and might not be too happy if you use them as your own test devices 👊

~/IOTA$ java cfb.ict.Ict ict.properties

We have specified the main class ICT to be called in cfb.ict and hand over the file ict.properties. The output is not that spectacular as we have no incoming traffic yet.

[2018-07-22T11:28:30.883] Ict 0.1.1
2018-07-22T11:28:31.049
Number of transactions = 0
Number of missing transactions = 0

Next we are going to send a message to the ICT via UDP from a different f1-micro instance. Which means just set up another cloud f1-micro instance or use any other linux distribution which is connected to the internet and is not blocked by your ICT client.

The easiest way to send an UDP packet to your ICT client I found is this

$echo "Hello ICT" > /dev/udp/YOUR_ICT_IP/11111

Which results on your ICT side (once a minute has passed) to immediate boring response

[2018-07-22T11:28:30.883] Ict 0.1.1
2018-07-22T11:28:31.049
Number of transactions = 0
Number of missing transactions = 0

2018-07-22T11:30:11.848
Number of transactions = 0
Number of missing transactions = 0

In order to make sure we receive the packet lets rewrite the code which prints our transmitted UDP message. After the byte[] data = datagramPacket.getData(); we add the following in order to print the incoming packets.

String string = new String(data, 0, datagramPacket.getLength());
System.out.println(string);
System.out.println("----");

In addition we like to see the IP of the sender and we add after the final SocketAddress socketAddress = datagramPacket.getSocketAddress(); the following code to print the address

System.out.println("SocketAddress =" + socketAddress);

After compiling the ICT.java and moving the *.class file to cfb/ict/ we can rerun our recompiled and changed ICT. Once we send four times the same message again we get this result (leave it a minute time to get the last lines):

[2018-07-22T11:46:39.484] Ict 0.1.1
2018-07-22T11:46:39.585
Number of transactions = 0
Number of missing transactions = 0
Hello ICT
----
Hello ICT
----
Hello ICT
----
Hello ICT
----
2018-07-22T11:47:44.403
Number of transactions = 0
Number of missing transactions = 0

We get the messages but no information about the IP address. Revisiting the code that is due to not getting passed this statement:

if (datagramPacket.getLength() != 1650) {
datagramPacket.setLength(1650);
} else {

Constructing a message with 1650 chars should be easy with linux, we can even use the SEED generation function and extend to 1650 in order to just get capital A...Z9

cat /dev/urandom |tr -dc A-Z9|head -c${1:-1650} > test.tx

we can’t use our echo command here as it breaks our 1650 length message into smaller messages. But we can do the following on our non-ict instance

cat test.tx > /dev/udp/10.142.0.4/11111

The result actually won’t print the complete message but we get more

{deleted: content of test.tx 1650 chars long}
----
SocketAddress =/IP:39025
2018-07-22T11:59:59.169
/IP:39025: 2 / 0 / 1 / 0 / 0
Number of transactions = 0
Number of missing transactions = 0

The bold marked line from the above ICT report is new and needs to be understood — looking at the code Alex from the official developer Discord did a thorough summary. Which at least one of those statement we have just proven invalidTxs ourselfs

Description of ICT logs by Alex
  • allTxs = number of UDP packages received at the specified port (default=14265) during a 1min period.
  • newTxs = number of valid transactions, that weren't received before during a 1min period.
  • invalidTxs = number of UDP packages, that don't form a proper transaction (invalid value, invalid address, invalid timestamp, i.e. before Mon Oct 23 12:00:00 UTC 2017, invalid transaction hash).
  • requestedTxs = number of times the ICT could answer a request for a certain transaction from this neighbor.
  • sharedTxs = number of new transactions, that were relayed to that neighbor during a 1min period.
  • Number of transactions = sum of all newTxs of all neighboring nodes during a 1min period.
  • Number of missing transactions = number of all requests, the ICT couldn't answer during a 1min period.

And we can extend the statement by our observation. The number of invalidTxs will not increase further than one. As you can see above, I’ve send two times the test.tx invalid transaction and the counter stopped at one. This is most likely as the code for validation of the transaction is not entered again in that epoch (1min. period) once a invalid transaction is found.

Summing it up: We have used online decompilers to look at the ICT java code which we then recompiled using standard java compiler and let it run again. We send UDP packets to our recompiled ICT and analysed by that the first few lines of the code.

In the next chapter I am planning (try) to get deeper into the logics of ICT and show you how we can identify invalid transaction and understand why they are getting pass IRI and ICT at the moment.

I’d love to see your questions doubts or corrections

Cheers,
Jan

--

--