Saturday, February 16, 2019

Hacking a Crouzet PLC (Zelio, Schneider)

I was given a Crouzet PLC, which turned out to be the same as other more popular brands like Zelio. Some time ago I successfully built a cable with a TTL-USB converter. I got the instructions from these links:

However after establishing a good connection with the PC I couldn't do anything with it. I come up with a solution in this post.

The problem
My PLC is a Crouzet CD12 model 88970823. For some stupid reason this PLC with this particular model number is a custom one, made (or locked) for one specific customer. I am completely convinced it's the exact same one as the readily available 88970042, however because of the difference in model number, the programming software (M3 soft) says it is unsupported and so, it don't allow me to do any damn thing to this piece of shit. So that was the problem.

Fail attempts
I thought of several approaches to solve this. Starting for the simpler, I tried to hack the M3 soft program itself at first but there was no file that I could easily edit to add my model to the supported list. Moreover, I couldn't easily disassemble the code.

The hack
The second easiest thing I came up with was to develop a simple man-in-the-middle program that controls the communications through the COM port, so when the time came I could fake the identity of my PLC and make the computer believe it's a supported device. To do this, first I used the free tool "null modem emulator (com0com)" that allowed me to create a virtual pair of COM ports so I can connect M3 soft to my program. Then, I wrote a Python script that did the work and talk directly to the PLC.

That was one cool and fun approach btw. The diagram is like this:

Sniffing the communication
I programmed a sniffer in Python to log the traffic of the COM port, which also was a a man-in-the-middle but for then, it was a transparent logger. The COM parameters as baudrate, data bits... where found in the internet. To be exact, in the following link, which is the datasheet of a compatible HMI for the Zelio (which is the same one under another brand).

Baudrate: 115200
Data bits: 7
Parity: even
Stop bits: 1

Playing with the M3 soft I could see how the traffic flowed.

Data from M3 soft at 14:30:49.385069 -> b':010300006D00018E\r\n'
Data from PLC at 14:30:49.433699 -> b':01030100FB\r\n'
Data from M3 soft at 14:30:49.455736 -> b':011000006C00020E0073\r\n'
Data from PLC at 14:30:49.493760 -> b':011000006C000281\r\n'
Data from M3 soft at 14:30:49.506650 -> b':0103000066004056\r\n'
Data from PLC at 14:30:49.558686 ->':010340434431325330373032302020383839373038
Data from M3 soft at 14:30:49.572890 -> b':0103000066404016\r\n'
Data from PLC at 14:30:49.617998 -> b':01034004D44700000000000020202020202020202020202
Data from M3 soft at 14:30:49.629034 -> b':01030000668040D6\r\n'
Data from PLC at 14:30:49.678148 -> b':010340000000000000000000000000000000000000000000
Data from M3 soft at 14:30:49.689183 -> b':0103000066C004D2\r\n'
Data from PLC at 14:30:49.733299 -> b':01030400000000F8\r\n'
Data from M3 soft at 14:30:49.744307 -> b':011000006C00020F0072\r\n'
Data from PLC at 14:30:49.793846 -> b':011000006C000281\r\n'
Data from M3 soft at 14:30:49.804854 -> b':011000006C00020E0073\r\n'
Data from PLC at 14:30:49.853793 -> b':011000006C000281\r\n'
Data from M3 soft at 14:30:49.878726 -> b':0103000066004056\r\n'
Data from PLC at 14:30:49.917554 -> b':0103404344313253303730323020203838393730383233010002080
Data from M3 soft at 14:30:49.939575 -> b':0103000066404016\r\n'
Data from PLC at 14:30:49.997619 -> b':01034004D4470000000000002020202020202020202020200000
Data from M3 soft at 14:30:50.008626 -> b':01030000668040D6\r\n'
Data from PLC at 14:30:50.058750 -> b':01034000000000000000000000000000000000000000000000000000000
Data from M3 soft at 14:30:50.070752 -> b':0103000066C004D2\r\n'
Data from PLC at 14:30:50.112383 -> b':01030400000000F8\r\n'
Data from M3 soft at 14:30:50.123392 -> b':011000006C00020F0072\r\n'
Data from PLC at 14:30:50.171425 -> b':011000006C000281\r\n'

The communication seemed to use the protocol Modbus Ascii, sort of. It used the same frame format but with custom instructions. To make sure I was right, I check if the checksum was calculated as expected with that protocol. The algorithm can be found in Wikipedia.

The checksum calculation matched the one in the frames I was sniffing so everything looked good.

Analysing the data
Apart from the communication format, I could not see anything interesting, so I kept playing with the M3 soft. Things got exciting when I pressed the option "Controller Diagnostics" and received this message:

That meant that when I pressed that button, the PLC sent the model number to the PC anyhow. The log of my sniffer showed lots of data, it wasn't clear where the number was nor how it was encoded.

I discarded some frames that where duplicated and after messing around for some time I found the damn think. I discovered that the number was send in ascii hex, so the model 88970823 looked like 3838393730383233! Do you see it?

Data from PLC at 14:47:43.559812 -> b':01034043443132533037303230202038383937303832330100

Faking the identity
That being so, I needed to inject the right model number in there, which was 88970042 or 3838393730303432 in hex. Also, I needed to regenerate the checksum once the data is modified so the M3 soft didn't complain (I tried it).

I modified my man-in-the-middle script to look for the request from the PC to send the PLC identity. Then it catches the frame from the PLC, injects the fake model number, regenerates the checksum and sends the info to the PC. The script doesn't messes with the rest of the data in the frame.

#Crouzet Hacker. Pedro Fernandez. Feb-2019

import serial, time
from datetime import datetime

#              port1    port2
portsAKAs = ["M3 soft", "PLC"]
enable_log = False

def log(data, source):
    if enable_log:
        timestamp = str(
        if data:
            print("Data from " + source + " at " + timestamp[11::] + " -> " + str(data), end='\n')

def modbusAsciiChecksum(frame):
    address = frame[1:3]
    function = frame[3:5]
    data = frame[5::]

    add = 0
    add += int(address, 16)
    add += int(function, 16)

    for i in range(0, len(data), 2):
        add += int(data[i:i+2], 16)

    add = -add
    add &= 0xFF
    result = str(hex(add).upper())
    return result[2::]

def generateInjection(frame):
    frame_decoded = frame.decode()      
    frame_decoded = frame_decoded.replace("3838393730383233", "3838393730303432") #replace 88970823 by 88970042

    #regenerate checksum
    frame_decoded = frame_decoded[0:-4] #crop old checksum and /r/n
    frame_decoded += modbusAsciiChecksum(frame_decoded)
    return bytes(frame_decoded, 'ascii') 

print("Crouzet Hacker. Pedro Fernandez. Feb-2019")
print("To stop press Ctrl + C.\n")

print("Enter M3 soft COM number: ", end='')
port1num = input()
print("Enter PLC COM number: ", end='')
port2num = input()

#Port inits
port1 = serial.Serial(port="COM" + str(port1num), baudrate=115200, bytesize=7, parity='E', stopbits=1)
port2 = serial.Serial(port="COM" + str(port2num), baudrate=115200, bytesize=7, parity='E', stopbits=1)

print("Waiting for data...")

IDrequested = False
    while True:
        #M3 soft
        if port1.in_waiting > 0:
            data_from_1_to_2 = port1.readline()
            log(data_from_1_to_2, portsAKAs[0])

            if data_from_1_to_2 == bytes(':0103000066004056\r\n', 'ascii'):
                IDrequested = True
                print("Identification requested by M3 soft")
        if port2.in_waiting > 0:
            data_from_2_to_1 = port2.readline()
            if IDrequested:
                data_from_2_to_1 = generateInjection(data_from_2_to_1)
                IDrequested = False
                print("Fake ID injected")
            log(data_from_2_to_1, portsAKAs[1])

except KeyboardInterrupt:
    print("Ports closed.")

Fortunately, I got it to work flawlessly. I tried to read and write to the PLC without any problem. I even updated the PLC firmware (this didn't modified the model number though).

Tell me in the comments if it helped or whatever.

Sunday, December 30, 2018

Disable Sleep Mode XYtronic LF-1600 Soldering Station

I have this soldering station which has served me well since I bought it. However, it has this annoying thing called 'sleep mode' which lowers the temperature to 150 ºC every 15 or 30 min. For me, that engages too often. Maybe they tried to mimic the sleep feature of top of the range stations, but since no sensor is in the stand to detect when you leave the iron, the implementation is poor and bothering.

I've always wanted to disable the feature, but in the owner's manual that came with my unit, the option seems to be missing. Now, I've found it explained in the manual of the model LF-1700 and I've discovered it's implemented in the firmware of my unit too. It's worth to mention that I couldn't understand shit from the chinenglish? of the manual, so that's the main reason to explain it properly here.

By the way, besides LF-1600 and LF-1700 this will probably work for more models.

So..., steps:

1. Turn on the unit.
2. Press SET for 4 seconds and '---' will blink in the display.
3. Press UP 10 times until '010' is in the display. This is the default 'config password'.
4. Press SET to enter the config menu.
5. Press DOWN several times until 'F-3' is in the display.
6. Press SET to enter.

At this point '100' should appear in the display, which means 'sleep mode enabled'.

7. Press UP or DOWN and the display will change to '000', which means 'sleep mode disabled'.
8. Press SET twice to exit.

Tell me in the comments if it helped.

Wednesday, December 19, 2018

All possible combinations in a 7 segment display

I was playing around with a display of 7 segment digits and was looking for some cool shape I could print in it. I wasn't been very creative and I noticed that, after all, only 128 combinations can be made with those kinds of displays. So I decided to write a dirty Python 3 script for generating all possible combinations, and here I show you the output.

I also leave here the dirty script.

import os

width = 20 #how many each row
q = 128 #how many in total

def gen7seg(segs):
    line1 = ""
    line2 = ""
    line3 = ""

    line1 += " "
    if( (segs >> 6) & 1): line1 += "_ "
    else: line1 += "  "
    if( (segs >> 1) & 1): line2 += "|"
    else: line2 += " "
    if( (segs >> 0) & 1): line2 += "_"
    else: line2 += " "
    if( (segs >> 5) & 1): line2 += "|"
    else: line2 += " "

    if( (segs >> 2) & 1): line3 += "|"
    else: line3 += " "
    if( (segs >> 3) & 1): line3 += "_"
    else: line3 += " " 
    if( (segs >> 4) & 1): line3 += "|"
    else: line3 += " "

    return [line1, line2, line3]   

printed = 0

finished = False
while(not finished):
    out_line1 = ""
    out_line2 = ""
    out_line3 = ""
    for _ in range(width):
        [a, b, c] = gen7seg(printed)
        out_line1 += (" " + a)
        out_line2 += (" " + b)
        out_line3 += (" " + c)
        if(printed < q-1): printed += 1
            finished = True


Speak if it was useful for something.

Saturday, November 17, 2018

Hair dryer schematic (Babyliss ioniCeramic 1600)

I troubleshooted a hair dryer whose heating elements got red in the high speed. The problem was quite obvious once disassembled, the heating element wires slightly deformed and short-circuited at one spot. Separating them did the job.

I felt like making a schematic to understand exactly how it (and other hair dryers) worked, since I more o less imagined it, but never confirmed it.

So, in case anyone need it here it is. It's not 100% complete since things like values and LED resistors are missing, but they don't really do much difference.

Tell me if it helped in the comments.

Saturday, July 28, 2018

Superlux HD662 EVO review vs. HD668b

I am a satisfied owner of the 668b, I’ve been using then for a long time (I wrote a review here when I got them) and now I want to tell you what the differences are respect to the 662 EVO, which is very handy if you plan to buy one of these.

If you’ve read any review of any of the contestants you’ll know the basics, like the 668b are semi-opened vs the closed 662, the differences in technical numbers, the 662 includes spare velour pads… I’ll leave that bullshit apart and will try to focus on the important things that you can’t find easily in the internet.

First things first, the sound: it’s very good and noticeably different from the 668b’s. The treble is brighter and sparky, but not bothering high as someone say, it’s just a bit louder. The mids stay ok, no problem with that. The bass is noticeably boosted in the lower frequencies of the bass region, but again, not disproportionate at all. The overall sound is great, the boosted bass is very very pleasant compared with the more flat response of the 668b.

 The insulation of the closed back headphones versus the 668b is dramatic. I’ve only tried the ‘leather’ pads and they insulate good when they push against your head. People can still hear sound escaping if you turn the volume up too much, but believe me, the insulation is good and you can’t expect cero sound escaping with high volume with any headphone. That being said, without a doubt I affirm the 662 are office/library suitable.

One thing that really surprised me was the quality, as the internet said it was poor. Not to me. The building quality is very good, the structure of the 668b is much more flimsy and flexible, but the 662 are more rigid and feel better.

Pads and comfort
The velour pads are just that, velour. They aren’t my ‘cup of tee’. The ‘leather’ ones have nothing to be with the 668b plastic ones. The 668b’s are thick plastic and the 662’s are soft thin leather. The force that the headphones put in your head is the right amount, being a bit too much at first but it’s expected that overtime they get more loose. Overall, they’re noticeably more comfortable that the 668b and less warm.

The price difference isn’t significant so it’s no factor. Moreover, the value for money is higher in the 662.

If you want good headphones to listen to music with nice bass, nice look and good insulation get the 662. They are now my recommendation for general purpose. If you need for some reason a flat response (audio mixing/producing) I definitely wouldn’t use the 662, in that case, 668b.
Hope it helps.

Sunday, June 3, 2018

Save as PDF vs. print Adobe PDF (MS Word)

I've wondered some times what was the difference between the two and today I found an important one.

Let's say you are working with a document in Word that you plan to convert to PDF. You're using a font that you want to embed in that PDF because it's a particular font and probably isn't installed in the target user computer.

  • If you 'Save as PDF', by default the font is 'embedded' by converting the letters to bitmap so it's a fail. This usually happens when the font isn't a 'common' one. Obviously, you won't be able to copy from this generated PDF.
  • If you print with 'Adobe PDF' virtual printer, the font is embedded properly and letters are vectorized.

So now, you know. If you're using weird special fonts, don't forget to print as PDF.

Saturday, May 26, 2018

Fix slow Windows Explorer loading/hanging/freezing not showing thumbnails W8.1

I had this issue with Windows Explorer and was a pain in the ass I couldn't stand any longer. So I did some research in the net and nothing really worked for me so I want to share what did the trick.

How to check if you have the same problem as me:

- Provoke a Windows Explorer hang and then open Event Viewer.
- Go to Windows Logs -> System
- Check if an error like this just happened:

Source: DistributedCOM Event ID: 10010
The server {1B1F472E-3221-4826-97DB-2C2324D389AE} did not register with DCOM within the required timeout.

If you get that, you have the same problem and probably the same solution will work.

What's fucking my explorer?

It's the stupid SkyDrive. The following steps are intended for disabling it, so if you really use SkyDrive, don't continue. But no one uses that shit.

Straight fix:

- Go to your local drive -> Windows -> System32
- Find SkyDrive.exe. Right click, Properties, tab 'Security', Advanced.
- Change the owner of the file from 'TrustedInstaller' to an administrator account that you can use. If your current user has admin rights, just change the owner to your current account.
- Below, change the administrators Access to 'Full control'. Apply and accept everything.
- Now you can rename SkyDrive.exe to whatever like fuckSkydrive.exe, so now it won't work any longer.

Restart your computer and your problem is probably solved.

Other things I did but should not make any difference:

If the above doesn't work, you can try this, but with SkyDrive disabled, this is probably pointless.

- Open Registry Editor.
- Go to HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Skydrive
- There, create the following DWORD entries and set them all to 1.


Tell me in the comments if this post helped.