TL;DR; Short the ERASE pin with VDDCORE, if ERASE == PIN_55 && VDDCORE == PIN_54

According to complains in the internet, users report bricking their Proxmark3 EASY, when they try to flash the latest firmware with the ‘flasher’ software tool.

Sometimes flashing process of firmware can go wrong, but it can often be recovered with JTAG programmers, or similar programmers.

I will not about setting up the environment to build, and flash the firmware, but I will tell you what you might be missing out and why it might be not working.

If you do not know where to start with flashing your Proxmark3, than have a look here, here, here or here. The first link describes the standard way of upgrading your firmware, which can fail, if you are unlucky. The other three links describe ways to recover your Proxmark3.

Why can upgrading the firmware fail? There are quite some reasons it can go wrong.

  • bad firmware image
  • wrong parameters
  • power loss
  • chip security measurements

With the Proxmark3 EASY it seems, that some devices have the Security Bit of the AT91SAM7S512 processor set. The datasheet (see page 113, paragraph 19.2.4.5) says: “The goal of the security bit is to prevent external access to the internal bus system. […] JTAG, Fast Flash Programming and Flash Serial Test Interface features are disabled. Once set,this bit can be reset only by an external hardware ERASE request to the chip. […]”.

To unlock the chip again we can find interesting information in this document on page 20 in paragraph 2.5. Which describes the unlocking the chip by applying Vcc to the ERASE pin. Applying voltage to the pin will wipe the security bit, but also all contents of the flash!

Unfortunately the ERASE pin, which is pin number 55 on the AT91SAM7S512, has no connection. The first idea was to solder a jumper wire to Vcc. On second guess and looking at the datasheets again, reveals pin 54 is VDDCORE, which applies 1.65V to 1.95V (1.8V typical) to the CPU for operation.

To erase and reset the Proxmark, I shortened pin 54 and pin 55 with the tip of a multimeter, applied power via USB to the Proxmark3. After >300ms the flash and security bit is erased and the device can be powered off.

The JTAG interface is now enabled again. Next I flashed the bootloader, and the fullimage using the Bus Pirate v4 using as described in one of the first links mentioned above.

#hackinghackertools

#MemeFreeEdition

The Challenge

You are provided a zip file containing two files.

  • program
  • vm.py

The file program contains instructions encoded as emojis for a virtual machine called vm.py. At the bottom of vm.py I found a list, or lets call it a translation table, with emojis and their function name counter part.

OPERATIONS = {
        '🍑': add,
        '🀑': clone,
        'πŸ“': divide,
        '😲': if_zero,
        'πŸ˜„': if_not_zero,
        'πŸ€': jump_to,
        'πŸš›': load,
        'πŸ“¬': modulo,
        '⭐': multiply,
        '🍿': pop,
        'πŸ“€': pop_out,
        '🎀': print_top,
        'πŸ“₯': push,
        'πŸ”ͺ': sub,
        'πŸŒ“': xor,
        'β›°': jump_top,
        'βŒ›': exit
}

To execute program with vm.py I ran:

$ python vm.py program
http://^C

The program started printing letters to the terminal, but became horribly slow, until I decided to terminate the execution. So the challenge seemed to be, to convince program to give me its secret. With the first characters looking like an URL, the flag might be hidden within the URL or on the website the URL pointing to.

In which way I had to convince the program to reveal its secrets to me, I had yet to decide. One way could be to optimize the VM code, the other might be optimizing the code of program.

The Solution(s)

Solving the Challenge: Trail and Error

The python script vm.py defines a class named VM with the following constructor:

def init(self, rom):
self.rom = rom
self.accumulator1 = 0
self.accumulator2 = 0
self.instruction_pointer = 1
self.stack = []

The VM stores the program code in the variable self.rom. It is filled on creation by reading the source file (program) given as argument. The source is stored as a list (returned by .read().split() of a file object). Two accumulator registers, self.accumulator1 and self.accumulator2, are initialized with ‘0’. A third register is the self.instruction_pointer, it will the the VM which operation in self.rom will be executed next. It starts at ‘1’ and not at ‘0’. The list containing the rom is initialized with an empty string (['']) and then extended by the program code. The last variable is self.stack which is empty list.

Lists in Python can be used as a stack like structure. With list.append() (push) and list.pop() (pop) the behavior of these data structures is identical.

I decided to translate the emoji code of program into a more human readable version as a beginning. A dictionary of all operations is at the bottom of the code.

The first version of my script did not translate all emojis in the code. I started digging around further in vm.py to find some more emojis I had to consider. As a result a almost completely emoji free version of the program could be generated. The “final” version of my script emoji2mnemonic.py had the following dictionary to translate program.

OPERATIONS = {
    '🍑': 'add',
    '🀑': 'clone',
    'πŸ“': 'divide',
    '😲': 'if_zero',
    'πŸ˜„': 'if_not_zero',
    'πŸ€': 'jump_to',
    'πŸš›': 'load',
    'πŸ“¬': 'modulo',
    '⭐': 'multiply',
    '🍿': 'pop',
    'πŸ“€': 'pop_out',
    '🎀': 'print_top',
    'πŸ“₯': 'push',
    'πŸ”ͺ': 'sub',
    'πŸŒ“': 'xor',
    'β›°': 'jump_top',
    'βŒ›': 'exit',
    'βœ‹': ';',
    'πŸ₯‡': '[acc=1]',
    'πŸ₯ˆ': '[acc=2]',
    'πŸ’°': '@',
    '😐': 'end_if',
    'πŸ–‹': 'func_',
    'πŸ€': 'BRK<',
    'β›°': 'BRK!',

    'πŸ’ πŸ”ΆπŸŽŒπŸš©πŸ': 'Label01',
    'πŸ’ πŸπŸŽŒπŸ”ΆπŸš©': 'Label02',
    'πŸš©πŸ’ πŸŽŒπŸ”ΆπŸ': 'Label03',
    'πŸπŸš©πŸŽŒπŸ’ πŸ”Ά': 'Label04',
    'πŸ”ΆπŸŽŒπŸš©πŸ’ πŸ': 'Label05',
    'πŸŽŒπŸπŸš©πŸ”ΆπŸ’ ': 'Label06',
    'πŸ”ΆπŸš©πŸ’ πŸπŸŽŒ': 'Label07',
    'πŸš©πŸ”ΆπŸπŸŽŒπŸ’ ': 'Label08',
    'πŸŽŒπŸš©πŸ’ πŸ”ΆπŸ': 'Label09',
    'πŸŽŒπŸπŸ’ πŸ”ΆπŸš©': 'Label10',
    'πŸπŸ’ πŸ”ΆπŸš©πŸŽŒ': 'Label11',
    'πŸ’ πŸŽŒπŸπŸš©πŸ”Ά': 'Label12',

    '0️⃣ ': '0',
    '1️⃣ ': '1',
    '2️⃣ ': '2',
    '3️⃣ ': '3',
    '4️⃣ ': '4',
    '5️⃣ ': '5',
    '6️⃣ ': '6',
    '7️⃣ ': '7',
    '8️⃣ ': '8',
    '9️⃣ ': '9',
}

The language for vm.py also knows labels for the jump_to operations and a symbol to distinguish between the accumulators to use.

A look at the translated source was a little bit more helpful. The example below shows a cleaned up, but shortened version of my interpretation of the syntax after translation.

load [acc=1] 0;
push [acc=1]
load [acc=1] 17488;
push [acc=1]
load [acc=1] 16758;
push [acc=1]
load [acc=1] 16599;
push [acc=1]
load [acc=1] 16285;
...
push [acc=1]
load [acc=2] 1;

func Label01
pop [acc=1]
push [acc=2]
push [acc=1]
load [acc=1] 389;
push [acc=1]
push [acc=2]
jump_to $[ Label04
xor
print_top
load [acc=1] 1;
push [acc=1]
add
pop [acc=2]
if_not_zero
jump_to $[ Label01
end_if
...

To find out what each operation does, I looked at the source code of vm.py. The load instruction reads as follows: “Load accumulator1 with the immediate value 0”. With push the value stored in the accumulator1 is pushed to the stack. Other operations take the top two items from the stack to process them (addition, subtraction, multiplication, division, modulo, xor, …).

By only looking at the code I had no clue what was going on, but with all the jumps, I though about visualizing the program flow. I used my translated program and formatted the code to be a compatible Graphviz dot file. The resulting graph was again a little bit more of an help.

Call graph of program

The graph shows a bunch of instructions to fill the stack of the VM. The right side has some more “complicated” instructions with modulo, division, xor, ….

When I see xor instructions, I often think of encryption and with multiplications and modulo instructions right next to it this though hardens.

But starring at the graph and the translated code did not help.

Solving the Challenge: Finally

It took me a while of starring until I realized, that I should try something else.

My next approach to solve this challenge was to write a trace log of the program execution step by step.

In each instruction function in vm.py I added some additional code to store the current status of the registers self.instruction_pointer, self.accumulator1, self.accumulator2 and translated each operation into its human readable counter part. I also added (if possible) the parameters and the result of calculations.

00000002 [acc1=00000000][acc2=00000000]: load("acc1", 0)
00000006 [acc1=00000000][acc2=00000000]: push(acc1) = 0
00000008 [acc1=00017488][acc2=00000000]: load("acc1", 17488)
00000016 [acc1=00017488][acc2=00000000]: push(acc1) = 17488
00000018 [acc1=00016758][acc2=00000000]: load("acc1", 16758)
00000026 [acc1=00016758][acc2=00000000]: push(acc1) = 16758
00000028 [acc1=00016599][acc2=00000000]: load("acc1", 16599)
00000036 [acc1=00016599][acc2=00000000]: push(acc1) = 16599
00000038 [acc1=00016285][acc2=00000000]: load("acc1", 16285)
...
00000386 [acc1=00000389][acc2=00000001]: push(acc2) = 1
00000388 [acc1=00000389][acc2=00000001]: jump_to(Label04) @1040
00001041 [acc1=00000002][acc2=00000001]: load("acc1", 2)
00001045 [acc1=00000002][acc2=00000001]: push(acc1) = 2
00001048 [acc1=00000002][acc2=00000001]: jump_to(Label08) @1098
00001099 [acc1=00000002][acc2=00000001]: clone() = 2
00001100 [acc1=00000002][acc2=00000001]: load("acc1", 2)
00001104 [acc1=00000002][acc2=00000001]: push(acc1) = 2
00001107 [acc1=00000002][acc2=00000001]: sub(b=2, a=2) = 0
00001108 [acc1=00000002][acc2=00000001]: if_zero() = 0
00001109 [acc1=00000002][acc2=00000001]: pop_out()  // 0
00001110 [acc1=00000001][acc2=00000001]: load("acc1", 1)
00001114 [acc1=00000001][acc2=00000001]: push(acc1) = 1
00001115 [acc1=00000001][acc2=00000001]: end_if = πŸ€
00001115 [acc1=00000001][acc2=00000001]: …cont… if_zero()
00001116 [acc1=00000001][acc2=00000001]: jump_to(Label05) @1050
00001051 [acc1=00000001][acc2=00000001]: if_zero() = 1
00001055 [acc1=00000001][acc2=00000001]: end_if
00001057 [acc1=00000001][acc2=00000001]: pop_out()  // 1
...

There are still a few emojis left, but I did not care about them. With the trace log I could observe the program being executed and calculating the URL, or at least see the trace part where the print_top of the characters happens.

00001076 [acc1=00000002][acc2=00000001]: if_zero() = 0
00001077 [acc1=00000002][acc2=00000001]: pop_out()  // 0
00001078 [acc1=00000002][acc2=00000389]: pop(acc2) = 389
00001080 [acc1=00000002][acc2=00000389]: push(acc1) = 2
00001082 [acc1=00000002][acc2=00000389]: push(acc2) = 389
00001083 [acc1=00000002][acc2=00000389]: end_if = β›°
00001083 [acc1=00000002][acc2=00000389]: …cont… if_zero()
00001084 [acc1=00000002][acc2=00000389]: jump_top() @389
00000390 [acc1=00000002][acc2=00000389]: xor(b=106, a=2) = 104
00000391 [acc1=00000002][acc2=00000389]: print_top("h")
00000392 [acc1=00000001][acc2=00000389]: load("acc1", 1)
00000396 [acc1=00000001][acc2=00000389]: push(acc1) = 1
00000398 [acc1=00000001][acc2=00000389]: add(a=1, b=1) = 2
00000399 [acc1=00000001][acc2=00000002]: pop(acc2) = 2
00000401 [acc1=00000001][acc2=00000002]: if_not_zero() = 119
00000401 [acc1=00000001][acc2=00000002]: end_if = πŸ€
00000401 [acc1=00000001][acc2=00000002]: …cont… if_zero()
00000402 [acc1=00000001][acc2=00000002]: jump_to(Label01) @371

The print_top instruction takes the item from the top of the stack and prints it out. The stack only stores numbers, so the number is converted into a character before output. Right before the print_top is a xor.

The xor takes the two top items from the stack to get the result of ‘104’ which is the letter “h”. The number ‘106’ was pushed to the stack in the beginning, but where does the ‘2’ come from? This took be a while to find out. But only after letting the program run bit longer.

$ grep -A1 xor trace.log 
00000390 [acc1=00000002][acc2=00000389]: xor(b=106, a=2) = 104
00000391 [acc1=00000002][acc2=00000389]: print_top("h")
00000390 [acc1=00000003][acc2=00000389]: xor(b=119, a=3) = 116
00000391 [acc1=00000003][acc2=00000389]: print_top("t")
00000390 [acc1=00000005][acc2=00000389]: xor(b=113, a=5) = 116
00000391 [acc1=00000005][acc2=00000389]: print_top("t")
00000390 [acc1=00000007][acc2=00000389]: xor(b=119, a=7) = 112
00000391 [acc1=00000007][acc2=00000389]: print_top("p")
00000390 [acc1=00000011][acc2=00000389]: xor(b=49, a=11) = 58
00000391 [acc1=00000011][acc2=00000389]: print_top(":")
00000390 [acc1=00000101][acc2=00000389]: xor(b=74, a=101) = 47
00000391 [acc1=00000101][acc2=00000389]: print_top("/")
00000390 [acc1=00000131][acc2=00000389]: xor(b=172, a=131) = 47
00000391 [acc1=00000131][acc2=00000389]: print_top("/")
00000390 [acc1=00000151][acc2=00000389]: xor(b=242, a=151) = 101
00000391 [acc1=00000151][acc2=00000389]: print_top("e")
00000390 [acc1=00000181][acc2=00000389]: xor(b=216, a=181) = 109
00000391 [acc1=00000181][acc2=00000389]: print_top("m")
00000390 [acc1=00000191][acc2=00000389]: xor(b=208, a=191) = 111
00000391 [acc1=00000191][acc2=00000389]: print_top("o")
00000390 [acc1=00000313][acc2=00000389]: xor(b=339, a=313) = 106
00000391 [acc1=00000313][acc2=00000389]: print_top("j")
00000390 [acc1=00000353][acc2=00000389]: xor(b=264, a=353) = 105
00000391 [acc1=00000353][acc2=00000389]: print_top("i")
00000390 [acc1=00000373][acc2=00000389]: xor(b=344, a=373) = 45
00000391 [acc1=00000373][acc2=00000389]: print_top("-")
00000390 [acc1=00000383][acc2=00000389]: xor(b=267, a=383) = 116
00000391 [acc1=00000383][acc2=00000389]: print_top("t")

Again after a long round of starring at the output, it hit me. The argument a is always a prime number, but these are special primes. All primes are palindromes. The argument b is the value from the stack.

In my next step I wrote a script to calculate the palindrome primes and xor them with the values from the stack. I also discarded the idea to optimize the one of the components to solve this challenge.

As my final solution does not use the next code blocks anymore I will only highlight some ideas and problems I encountered.

I found a algorithm to calculate palindrome prime numbers on Stackoverflow and modified it to my needs.

def gen_pal_prime(a, b):
    for i in range(a, b):
        y = True
        if str(i) == str(i)[::-1]:
            if(i > 2):
                for a in range(2, i):
                    if i % a == 0:
                        y = False
                        break
                if y:
                    yield i

palindromes = [_ for _ in gen_pal_prime(0, 999999)]

The problem with this was, depending on the second parameter it takes a long time to calculate all primes and the other problem was, it did not work. Or it stopped working after the 39th character.

The algorithm failed to calculate the correct xor value for ‘93766’. As the string read until it broke “http://emoji-t0anaxnr3nacpt4na.web.ctfco” my guess was, that the next character has to be a ‘m’ (ctfcompetition).

Brute forcing the prime number for ‘93766’ returned ‘93739’. What left me puzzled again. The previous prime number was ‘17471’. So I split up the whole blocks of pushing numbers to the stack into three blocks.

+-----------+-----------+-----------+
| Block 1   | Block 2   | Block3    |
+-----------+-----------+-----------+
| 17488     | 98426     | 101141058 |
| 16758     | 97850     | 101060206 |
| 16599     | 97604     | 101030055 |
| 16285     | 97280     | 100998966 |
| 16094     | 96815     | 100887990 |
| 15505     | 96443     | 100767085 |
| 15417     | 96354     | 100707036 |
| 14832     | 95934     | 100656111 |
| 14450     | 94865     | 100404094 |
| 13893     | 94952     | 100160922 |
| 13926     | 94669     | 100131019 |
| 13437     | 94440     | 100111100 |
| 12833     | 93969     | 100059926 |
| 12741     | 93766     | 100049982 |
| 12533     | 99        | 100030045 |
| 11504     |           | 9989997   |
| 11342     |           | 9981858   |
| 10503     |           | 9980815   |
| 10550     |           | 9978842   |
| 10319     |           | 9965794   |
| 975       |           | 9957564   |
| 1007      |           | 9938304   |
| 892       |           | 9935427   |
| 893       |           | 9932289   |
| 660       |           | 9931494   |
| 743       |           | 9927388   |
| 267       |           | 9926376   |
| 344       |           | 9923213   |
| 264       |           | 9921394   |
| 339       |           | 9919154   |
| 208       |           | 9918082   |
| 216       |           | 9916239   |
| 242       |           | 765       |
| 172       |           |           |
| 74        |           |           |
| 49        |           |           |
| 119       |           |           |
| 113       |           |           |
| 119       |           |           |
| 106       |           |           |
| 1         |           |           |
+-----------+-----------+-----------+

The purpose of the last number of each block opened up to me, when I finally found the correct prime for the third block. To decode the correct URL I ignored these numbers (‘1′, ’99’, ‘765’) and started building special cases into my decoding function.

The code below almost worked fine. Each block would be separately being decoded, after a certain number of iterations (i). I tried to make some sense of these numbers at the bottom of each block.

print('Palindrome Primes #1')
palindromes1 = [_ for _ in gen_pal_prime(1, 18000)]
palindromes1.insert(0, 2)  # manually add the number 2.
print('Palindrome Primes #2')
palindromes2 = [_ for _ in gen_pal_prime(93766 - 99, 999999)]
print('Palindrome Primes #3') 
palindromes3 = [_ for _ in gen_pal_prime(9916239 - 765, 19916239)]

palindromes = palindromes1
stack.reverse()
for i, val in enumerate(stack):
    if i == 40:
        mode = 'stack2' 
        palindromes = palindromes2
    if mode == 'stack2':
        i -= 40
        if i == 14:
            mode = 'stack3'
            palindromes = palindromes3
    if mode == 'stack3':
        i -= 13
    c = val ^ palindromes[i]
    print(f'{i:<8d}| {val:8d} ^ {palindromes[i]:<8} = {c:3d}', chr(c))

While my program ran, I encountered yet again a problem but this time during decoding the third block of numbers. The calculation of my palindrome primes took quite some time and did not result in printable/human readable characters.

Talking to a colleague of mine, he came up with the idea to look up lists of precalculated prime numbers and I found a website which provides lists of the first 2 billion prime numbers as downloadable files.

The site http://www.primos.mat.br/2T_en.html provides individual compressed text files, with 10 million primes each file. For this challenge the prime numbers from 2 to 179424673 should suffice.

After decompressing the file I had to convert the file from columns separated by tabs to a easier to read format for my Python programs. To remove the columns and only have one prime per line I used the program tr.

$ tr '\t' '\n' < 2T_part1.txt > primes.txt

I wrote a function to read this file to only return the palindrome primes as a list. The str(_.rstrip()) == str(_.rstrip())[::-1] part of the condition will check if the number is a palindrome, by comparing the number as string against it reversed counterpart.

def load_primes(filename):
     with open(filename, 'rt') as handle:
         return [int(_) for _ in handle.readlines()
                 if str(_.rstrip()) == str(_.rstrip())[::-1]]

With this list of palindrome primes I wrote a short brute force solution to find the correct prime number to decode the third block, or to be more precise, where in this list of primes can I find it.

As a constraint I iterated through all primes until the prime p from primes is greater than the number (‘9916239’) I am looking for. Then I know, that my prime had to be within the first 765 primes.

>>> primes = solver.load_primes('primes.txt')
>>> for i, p in enumerate(primes):
...     if p > 9916239:
...         print(i)
...         break
...
765         

At this point the 3 numbers I could not figure out until now, now made sense. These are the n-th number prime from a list of consecutive palindrome primes. The first block begins at the the 1st prime from my list of primes, the second block begins at the 99th prime number and the third block at the 765th prime number from my list.

And now it also makes sense, why program becomes slower with each iteration. The code block (see the call graph above) calculates palindrome prime numbers, which becomes more and more of a problem regarding performance when it hits greater numbers.

How does my final solution look like? I wrote a script called solver.py, put all numbers from all three blocks into a single list and wrote my decryption function.

#! /usr/bin/env python
#-*- coding: utf-8 -*-

stack = [101141058, 101060206, 101030055, 100998966, 100887990,
         100767085, 100707036, 100656111, 100404094, 100160922,
         100131019, 100111100, 100059926, 100049982, 100030045,
         9989997, 9981858, 9980815, 9978842, 9965794, 9957564,
         9938304, 9935427, 9932289, 9931494, 9927388, 9926376,
         9923213, 9921394, 9919154, 9918082, 9916239,  # Block 3
         98426, 97850, 97604, 97280, 96815, 96443, 96354, 95934,
         94865, 94952, 94669, 94440, 93969, 93766,  # Block 2
         17488, 16758, 16599, 16285, 16094, 15505, 15417, 14832,
         14450, 13893, 13926, 13437, 12833, 12741, 12533, 11504,
         11342, 10503, 10550, 10319, 975, 1007, 892, 893, 660,
         743, 267, 344, 264, 339, 208, 216, 242, 172, 74, 49, 119,
         113, 119, 106]  # Block 1


def load_primes(filename):
    with open(filename, 'rt') as handle:
        return [int(_) for _ in handle.readlines()
                if str(_.rstrip()) == str(_.rstrip())[::-1]]


if __name__ == '__main__':
    palindromes = load_primes('primes.txt')
    stack.reverse()
    offset = 0
    for i, val in enumerate(stack):
        if i == 40:
            offset = 98 - 40
        if i == 54:
            offset = 764 - 54
        c = val ^ palindromes[i + offset]
        print(chr(c), end='')
    print()

Running this script will produce the URL “http://emoji-t0anaxnr3nacpt4na.web.ctfcompetition.com/humans_and_cauliflowers_network/“. Visiting this URL gives a choice for three sub pages of different people. The flag is stored in the personal page of Amber within a picture. The flag reads “CTF{Peace_from_Cauli!}“.

Lessons learned

Doing CTFs sometimes require you to think out of the box and/or ignore the first possible solutions which might seem to be the obvious ones, but might lead you nowhere or are just there to keep you side tracked.

This challenge first got me thinking about modifying and changing the behavior of the provided files program and vm.py. While working through the challenge, which kept me busy for quite some time, I found a different solution.

The previous tries to solve this challenge were not for nothing, especially the addition of my trace log addition, which finally helped me understanding the problem and getting my on the right track to the solution.

Software Requirements

SoftwarePurpose
dotPeekDecompiler for .NET programs

The implementation of the protocol is then written in Python. Let’s hear what the curator has to say:

Tell me about Python.

Wow. Much Snake. Easy programming!

– Doge

Tell me about dotPeek.

Easy decompilation. Readable syntax. Very easy.

– Doge

Analyzing MC3000_Monitor

Decompiling

Start dotPeek and use Drag’n’Drop to load the Application. Or uSe CtRL+O anD LoCATe tHe fILe uSiNg ThE bOrwsEr. I am not your mother! The file will show up in the Assembly Explorer. You can expand and collapse nodes within the tree. Familiarize yourself with the program structure. Try to find the entry point or other “important” functions, modules or resources.

If you right click the MC3000_Monitor node you can export the project with individual source files. This project is stored as *.cs source files.

You should now have either the project loaded into dotPeek or – in addition – saved it as project and/or loaded it into Visual Studio (VS) (Not covered here). I cannot afford VS. Still saving money to upgrade my IDA Pro license.

 Intermission:
 This is the curator speaking, and I command you to stop whining, 'JPK'.

As you can see, a lot of professionalism.

Exploring the code

For me the easiest way to begin, is to find parts of code where user interaction is required. Run the program and look at the user interface. In this particular case we have four buttons next to a label each.

Lets explore the code in dotPeek and see if we can find some code that might look familiar.

Pressing one of the buttons opens another window where you can configure the charger slot. By further reading through the different functions you might come across the function InitializeComponents():void. Each window element gets setup and function callbacks/events are registered.

You eventually find something like this (see the picture below).

Let’s put on our smart looking spec ticals and read line 3468 and 3470. Line 3468 is the creation of the button text, which should look familiar. If not, search for hints on this page. Line 3470 binds a function to the button press. With a Ctrl+Left click we jump to the function definition in dotPeek.

The function private void button1_Click_1(object sender, EventArgs e) is pretty simple to read. When the button is clicked, get the button name (e.g. “button1” [1]) and check if there is either the number 1, 2, 3 or 4 in the name.

Can you see the problem here? There is no error handling if button number is smaller than one or greater four. As an array is indexed, the program will probably crash. At this point we don’t care. We want to make our own library, to make it better or different. After the name checking to know which slot is addressed, it calls a function public void Set_Battery_Type_Caps(ChargerData[]data, int index). The functions sets the parameters of each battery slot and saves the values to an array.

This function sums up all parameters we need to know to setup a charger slot by or self. And we now know the default values. The below listing is the exception code, if anything goes wrong in the code above, but not when using an index outside bounds.

// Battery.cs:1195

data[index].Type = 0;
data[index].Caps = 2000;
data[index].Mode = 0;
data[index].Cur = 1000;
data[index].dCur = 500;
data[index].End_Volt = 4200;
data[index].Cut_Volt = 3300;
data[index].End_Cur = 100;
data[index].End_dCur = 400;
data[index].Cycle_Mode = 0;
data[index].Cycle_Count = 1;
data[index].Cycle_Delay = 10;
data[index].Peak_Sense = 3;
data[index].Trickle = 50;
data[index].Hold_Volt = 4180;
data[index].CutTemp = 450;
data[index].CutTime = 180;

The data structure ChargerData can be looked up as well, but the above listing is a little bit easier to read.

What we haven’t seen at this point were bytes transferred to or from the device.

At this point, there are multiple ways to get a good starting point on finding the functions where data is transmitted or received. One option is to look at the Assembly Explorer again for functions names of possible interest.

These convenient looking functions. Or should I say obvious function names are obvious, are used to handle the USB device communication. Try right clicking a function to find out where it is used. I have used usbOnDataRecieved. In the below window with search results you can find a reference located in the constructor [2] of the class FormLoader.

// FormLoader.cs:352

this.usb = new UsbHidPort();
this.usb.ProductId = 1;
this.usb.VendorId = 0;
this.usb.OnSpecifiedDeviceArrived += new EventHandler(this.usbOnSpecifiedDeviceArrived);
this.usb.OnSpecifiedDeviceRemoved += new EventHandler(this.usbOnSpecifiedDeviceRemoved);
this.usb.OnDeviceArrived += new EventHandler(this.usbOnDeviceArrived);
this.usb.OnDeviceRemoved += new EventHandler(this.usbOnDeviceRemoved);
this.usb.OnDataRecieved += new DataRecievedEventHandler(this.usbOnDataRecieved);
this.usb.OnDataSend += new EventHandler(this.usbOnDataSend);

These lines above register event handlers with an instance of UsbHidPort. An event might be connecting or disconnecting the device (line: 355-358) or transferred data (line: 359-360). There is nothing special about the connect functions, except for usbOnSpecifiedDevice... ones. There is a call to stop and stop the timer2 instance. We will look at this object in a second, but first we have a look at usbOnDataSend and usbOnDataRecieved.

// FormLoader.cs:513

private void usbOnDataRecieved(object sender, DataRecievedEventArgs args)
{
  if (this.InvokeRequired)
  {
    try
    {
      this.Invoke((Delegate) new DataRecievedEventHandler(this.usbOnDataRecieved), sender, (object) args);
    }
    catch (Exception ex)
    {
      Console.WriteLine(ex.ToString());
    }
  }
  else
  {
    ++this.packet_counter;
    for (int index = 0; index < 65; ++index)
      this.inPacket[index] = args.data[index];
    this.dataReceived = true;
  }
}

...

// FormLoader.cs:580

private void usbOnDataSend(object sender, EventArgs e)
{
  this.Text = "ChargeMonitor V2 Connect";
  this.label_usb_status.Text = "USB ON";
  this.label_usb_status.ForeColor = Color.Green;
}

The usbOnDataSend function is boring, and we can ignore it. There is no active sending of data to the usb device. UsbOnDataReceived on the other hand is actually doing something with an buffer of 64 bytes (line: 528-531).

When data is received an internal packet_counter is increased. Each packet has a size of 64 bytes (line: 529). The packet is copied into the inPacket array, and the dataReceived variable is set to true.

My guess is, that somewhere, something, somehow, might be, is waiting for a packet to arrive and waits until dataReceived is true. In dotPeek we can use the magic function “Find Usages” again, to find out more.

Prototyping and understanding the program

Remember the timer2 instance mentioned before? No, try to find the hint on this page.

// Formload.cs:1219

private void timer2_Tick(object sender, EventArgs e)
{
  byte num1 = 0;
  if (!this.dataReceived)
    return;
  this.dataReceived = false;
  if ((int) this.inPacket[1] == 240)
{
  if (this.bwrite_chrager_data)
    return;
  int num2 = (int) MessageBox.Show("OK!");
}
else if ((int) this.inPacket[1] == 95)
  this.Get_Charge_Data((int) this.inPacket[2]);
else if ((int) this.inPacket[1] == 90)
{
  this.Get_System_Data((int) this.inPacket[3]);
}
else
{
  if ((int) this.inPacket[1] != 85)
    return;
  for (int index = 1; index < 64; ++index)
    num1 += this.inPacket[index];
  if ((int) num1 != (int) this.inPacket[64])
    return;

The timer2 will process incoming data send from the device to the connected computer. The code begins with comparing index 1 of the inPacket with a series of values.

By looking at the code we might be able to assume we are looking at the first bytes necessary to communicate with the device. Here are some guesses:

ValueDescription
240 (0xf0)Confirmation sent by charger.
95 (0x5f)Get charger data by information provided in index 2, which is an number [4].
90 (0x5a)Get system data by information provided in index 3, which is a number.
85 (0x55)Do not process the packet here. Otherwise calculate the sum of all bytes in num1 and compare it to the information stored in index 64.

If these checks do not result in an premature return, the values from inPacket are copied into variables. Some variable names are recognizable and help in our guessing game to find out what this function does.

With the looks of it we are reading battery information. As an example on how the packet is decoded, we will have a look at the following code:

this.Current[j] = (int) this.inPacket[11] * 256 + (int) this.inPacket[12];

The contents of inPacket index 11 and 12 is assigned the the variable Current at index j. Which is irrelevant at this point. But we need to understand what is happing with this multiplication and addition.

The multiplication by 256 is just a different way to express a left shift by 8. What happens, when we take the value 1 and multiply it by 256 or do a left shift by 8? In binary representation it will become very easy to understand.

     1   =>         0b1
   256   => 0b100000000

So what if we take 256 times 1?

   256   => 0b100000000

And if we take the value 0b1 and move the 1 exactly 8 positions to the left, like a left shift, duh?

   1 << 0 = 0b1
   1 << 1 = 0b10
   1 << 2 = 0b100
   1 << 3 = 0b1000
   1 << 4 = 0b10000
   1 << 5 = 0b100000
   1 << 6 = 0b1000000
   1 << 7 = 0b10000000
   1 << 8 = 0b100000000

This is just a step by step illustration. Computer do fast. Computer do in single step.

After the left shift, the second value is added to the variable. In other words, we are reading two bytes and concatenate it to have a word (2 bytes).

The same applies to the other functions Get_Charge_Data and Get_System_Data where the inPacket is read.

But how am I supposed to create my own library with this?

This is the part, where you take your favorite language, or a language good for prototyping and begin coding. First challenge would be to connect to the USB device. I am using the pyusb module with Python.

To connect an USB device we want to make sure we are using the right one. To do so, the USB devices can be identified by different properties, and one of them is the vendor and product id. The source of the program might give us enough information we need, as it needs to connect to the charger as well.

The product and vendor id can be found in the function private void usbSendConnectData() and is defined as:

FieldValue
Vendor ID0
Product ID1

Reading Data

The people writing the firmware for the charger, did not care to give it some nice values, on the other hand 0 and 1 are nice. With these identifiers, it is possible to connect to our charger.

import usb
usb.core.find(idVendor=0, idProduct=1)

This will return a list of devices, even when the list is empty or contains just one element. Set up the USB device further and start building your first packet to send. Before blindly sending commands to the charger, what would be the most destructive – errr – non destructive command: getting device information.

Do some reads on the device without sending anything to it. Eventually you will receive a packet.

In this scenario the packets have a common format for receiving and sending. You might notice a 0x0f at the beginning of each packet. As dotPeek is unable to tell you where it comes from and where it is designed, I am going to spoil it for you.

In file FormLoader.cs in line 201 we find the following definition:

public const byte UART_DATA_START = 15;

The UART [6] we are basically telling the charger we are coming over USB. There is also a mobile application to send commands via Bluetooth, but I haven’t done this one, yet.

There is function called Get_System_Data. When we look at the definition of the function the code is very messy.

Alot [5]_ of constant values are assigned to variables, which are assigned to variables and then used as index. This looks confusing but the best way is to just begin prototyping it the same way.

num1 = 0
str1 = ''
num2 = 4
# Do not kown this yet
inPacket = raw_packet  # raw_packet is the contents read by pyusb.
index1 = num2
num3 = 1
num4 = index1 + num3
num5 = int(inPacket[index1], 16)  # Python 3: probably bytes as input.
...

And so on. After building your prototyped function you will see parts which can be optimized, but do not care about it too much in the beginning. Try to understand how packets are constructed and what they contain. But for example the num2 = 4 could be removed and replaced with index1 = 4, as num2 is not used after that point.

By breaking the packet down, byte by byte (there are only 64 bytes), we then try to create data structures from it, like the one mentioned in the beginning. Each information gathered so far helps in decoding packets received and to later send packets.

For decoding packets I personally use the Python struct module. By reading the definition of Get_System_Data we define a system class, and machine_info as FormLoader.cs calls it.

With struct we define a data structure which can parse the 64 bytes each packet has and apply it to a named tuple in Python. After reading the original decompiled code, I came up with this definition:

#: Machine response data
MACHINE_INFO_STRUCT = '>3xBBBBBBBBBBBBB6sBBhBBBBbB'
#: Tuple for device information
MachineInfo = namedtuple('machine_info',
                         ['Sys_Mem1', 'Sys_Mem2', 'Sys_Mem3', 'Sys_Mem4',
                          'Sys_Advance', 'Sys_tUnit', 'Sys_Buzzer_Tone',
                          'Sys_Life_Hide', 'Sys_LiHv_Hide',
                          'Sys_Eneloop_Hide', 'Sys_NiZn_Hide', 'Sys_Lcd_time',
                          'Sys_Min_Input', 'core_type', 'upgrade_type',
                          'is_encrypted', 'customer_id', 'language_id',
                          'software_version_hi', 'software_version_lo',
                          'hardware_version', 'reserved', 'checksum',
                          'software_version', 'machine_id'])

The struct definition MACHINE_INFO_STRUCT describes how each byte of the packet should be interpreted. In words:

  • We decode it as big-endian.
  • Ignore 3 bytes as these are protocol commands.
  • Read 14 unsigned bytes (0..255), each into a separate variable.
  • Read 6 characters or a string of length 6.
  • Read 2 individual bytes.
  • Read a short (2 bytes).
  • Read 4 individual unsigned bytes.
  • Read a signed byte (-128..127).
  • Read a unsigned byte.

The MachineInfo is a namedtuple, to make it very easy to assign and access values. When we receive a packet and we have determined the type, we can do something like this:

data = unpack(MACHINE_INFO_STRUCT, response[:32])
machine = MachineInfo(\*data, 0, machine_id)

Sending Data

While reading data is one side, we also need send commands. When optimizing the code the private bool Send_USB_CMD(int Solt, byte CMD) function can be annoying, but refactoring the prototype code will very quickly tell you where to place your bytes.

Whilst the original code is hiding the CMD parameter position behind some index calculations (which lies in nature of decompilation) we can translate the following code:

To a single byte-string if we use the Get_System_Data CMD code 95:

\x0f\x00\x5a\x00

One really annoying thing is the index counting. The program starts filling the outPacket at offset 1. Which is actually 0, which is always set to 0x0f. It is protocol definition.

Tricky thing is the real offset 1. It has to be set to a specific value. To find out which one, we have to further investigate the code. This changes depending on the operation you want to call.

Going further through the code, we might find a location where it sets the offset 1 to a other value than 0. Eventually the offset becomes 4. The command so far is now:

\x0f\x03\x5a\x00

Sending this to the device returns no result, therefore we are still missing something. Somewhere was a loop adding up all bytes of packet. This could be a checksum and/or the command is still incomplete. Let’s look at the Send_USB_CMD again. When working through the code, it is help full to take notes.

I have removed a switch-case statement for your convenience.

After working through the code, taking notes. the resulting packet for CMD=95, Solt=0 [7] should look like this:

\x00\x0f\x03\x5a\x00\x00\x5d\xff\xff

The two bytes of \xff (255) at the end define the end a packet. Every packet is produced after this schema.

ByteDescription
1It is always 0, you will learn soon enough why! ARRGHGGHG
2Start of message (Always 0x0f (15)).
3Calculate the value based on the index.
4The command op code.
5It is 0.
6The slot index (0 to 3 (4 Slots)).
7The sum of the variable data (Byte 3 to 6)
8Is always 0xff (255)
9Is always 0xff (255)

Sending this to the device is still not correct, why? To find out why, delving deeper into the nested classes we find an abomination. The decompiled code for SpecifiedOutputReport.cs in class UsbLibrary.SpecifiedOutputReport, there is this one function:

public bool SendData(byte[] data)
{
  byte[] buffer = this.Buffer;
  for (int index = 1; index < buffer.Length; ++index)
    buffer[index] = data[index];
  return true;
}

The line 19 defines a loop starting at index 1…

With all this knowledge collected the final valid packet to send to your device is:

\x0f\x03\x5a\x00\x00\x5d\xff\xff

That’s it. We have done it.

KTHXBYE!

  1. BTW, giving descriptive names for your variables is totally over rated.
  2. Bob, is it you? [3]
  3. Stupidest joke so far. He is no constructor, he is a builder.
  4. As you might have noticed. I am just reading and translating the code.
  5. |alot| this was an intentional typo.
  6. Universal Asynchronous Receiver/Transmitter
  7. Solt [SIC]

Sneak Preview

These letters. Such announcement. Many words.

In the next few days I will publish two – not one – but two articles on how to approach a problem on how to reverse engineer protocols. There have been to applications I looked into to code a library for my home uses.

#1 – MC3000 Charger

MC3000_Charger provides an USB and Bluetooth (BT) interface (Spoiler: I am not covering the BT interface. Not yet). The USB interface is used to update the firmware and to program and interact with the charger during charging.

The Windows software provided by SkyRC can program each slot individually to support different types of batteries with different charging capacities.

As a result of my analysis, and this will be one of the upcoming articles, I reversed the application and wrote a Python library. To do so I dissected a .NET application. So no big magic here!

#2 – LW12 WiFi LED Controller

This was a tricky one. It is a low budget Chinese WiFi LED controlled with a mobile app. The Android app I looked at was encrypted using a separate VM layer on-top of the Dalvik engine. (Spoiler: No need to reverse this, and I did not do it.)

Sometimes there are simpler solutions. This is what the second article will be about.

The controller itself comes by many names: Foxnovo and I remember buying it as a Lagute.

KTHXBYE.