Hardware hacking

Hi!

Just for the others that might have the same question:

Yes, there is a serial connector inside:

The edge connector (you can see it on the few board photos that you can find)

The active lines are on the disk-side of the board: Ground, Rx, Tx (Gap) 3.3V.

I might have exchanged Rx and Tx here (my cable is already fix inside the closed box)

Connect your Rx first and you will know.

the console uses 115200 baud.

If nothing helps, this might help to unbrick your MyCloud?

Interesting things while watching the boot messages:

Barebox waits for an ICMP packet for 5 seconds while booting:

This might be the easiest way to get into “bricked” My Cloud, that isn’t booting from the hard disk anymore.

You can grep the wd gpl barebox source code for “Magic”.

Bye!

  Baerle

1 Like

baerle wrote:

Hi!

 

Just for the others that might have the same question:

 

Yes, there is a serial connector inside:

The edge connector (you can see it on the few board photos that you can find)

The active lines are on the disk-side of the board: Ground, Rx, Tx (Gap) 3.3V.

If nothing helps, this might help to unbrick your MyCloud?

 

Interesting things while watching the boot messages:

Barebox waits for an ICMP packet for 5 seconds while booting:

This might be the easiest way to get into “bricked” My Cloud, that isn’t booting from the hard disk anymore.

You can grep the wd gpl barebox source code for “Magic”.

 

 

Very iInteresting. Any ideas how access via ICMP could happen?

Have you understood that magic code?

Not really. I’m constructing a cable that comes out of that box, trying not to destroy anything.

The barebox loader is waiting for 5s and watches for network packets:

I just noticed that message from the loader (obvious due to the delay!) and immediately greped the source.

As it is waiting before the kernel is loaded it is obvious that it is added to control the boot process.

Just to shorten the search in the code: that is what I’ve found. Next step is to look at RX_ICMP_ENV_VAR.

commands/rxicmp.c:static void rxicmp_handler(void *ctx, char *pkt, unsigned len)
{
        struct icmphdr *icmp = net_eth_to_icmphdr(pkt);

        if (icmp->type == ICMP_ECHO_REQUEST) {
                if ( strncmp( WD_ICMP_ENCODE_MSG,
                              net_eth_to_icmp_payload(pkt),
                              sizeof(WD_ICMP_ENCODE_MSG)) ) {
                        return;
                }
        }
        rxicmp_state = RXICMP_STATE_SUCCESS;
        printf("[INFO] WD Magical Packet Received!\n");
}a few lines later: /* XXX: getenv(RXICMP_ENV_VAR) can be replaced by the series of commands contained in rxicmp_startup_script. In that case there is no need for having '/env/bin/rxicmp_startup script' script and 'rxicmp_startup_env' environment variable within '/env/config'.*/ if ( rxicmp_state == RXICMP_STATE_SUCCESS ) { run_command(getenv(RXICMP_ENV_VAR), 0); }include/net.h:#define WD_ICMP_ENCODE_MSG "WD-ICMP-BEACON" /* Western Digital MSG Encoded within ICMP Pkt */
1 Like

and that should be this:

#!/bin/sh

rxicmp_startup_script - TFTP’s and executes startup.sh script required for

#                         initiating the rxicmp boot sequence.

dhcp
tftp startup.sh
/startup.sh
#tftp $rootpath/$bootfile /dev/mem
#bootm /dev/mem

So:

Theory: Sending that magic packet to the box will trigger a dhcp request, then a tftp download of another script.

and that can do whatever you can do in barebox:

Nice!

And I have a serial console attached, so I can look what is happening.

Maybe I should try to walk that way a bit more!

I was able to modify  a ping (that sends these ICMP Echo Requests) to send the required bytes. (The modification was not store a timestamp to the data).

Setting a static arp address helped to keep the IP → MAC address mapping:
sudo arp -i eth0 -s

then start to send the packets:

sudo ./ping -p 57442d49434d502d424541434f4e0000

Switch on the box, and wait a short moment and the box ask the DHCP server and will try to download (via tftp) startup.sh.

Until now, I used my MyCloud as tftp server (nice for PXE booting), so I have to setup one on my Linux machine to continue.

Now some source I hacked together.

“Installation”: on a linux machine, compile it with gcc -o rawping rawping.c

then call it with sudo ./rawping eth0

Without a serial terminal you might use wireshark to watch what happens.

/*
    ICMP ECHO Packet sender with WD ICMP "Magic" Payload
    Triggers WD MyCloud to load a 'startup.sh' file via tftp.
*/

/*
    Copyright (C) 2014 Uli Tessel (utessel@gmx.de)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <string.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <linux/sockios.h>
#include <linux/if.h>
#include <linux/if_packet.h>

struct MagicPacket
{
  struct ether_header eth;
  struct iphdr ip;
  struct icmphdr icmp;
  char data[56];
} __attribute__ ((packed));

char Magic[16] = "WD-ICMP-BEACON\0";


int main(int argc, char ** argv)
{
  int i;
  int fd;
  int result;
  int mac[6];
  struct ifreq ifDataIdx, ifDataMac;
  struct sockaddr_ll address;
  struct MagicPacket packet;

  memset( &mac, 0, sizeof(mac));
  memset( &ifDataIdx, 0, sizeof(ifDataIdx));
  memset( &ifDataMac, 0, sizeof(ifDataMac));
  memset( &address, 0, sizeof(address));
  memset( &packet, 0, sizeof(packet) );

  if (argc != 3)
  {
    printf("Usage: %s <interfacename> <mac-address>\n", argv[0] );
    return -1;
  }

  result = sscanf( argv[2], "%02x:%02x:%02x:%02x:%02x:%02x",
    &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5] );

  if (result != 6)
  {
    printf("invalid (%d) MAC Address %s\n", result, argv[2] );
    return -1;
  }

  fd = socket( AF_PACKET, SOCK_RAW, IPPROTO_RAW);
  if (fd == -1)
  {
    perror("Creating raw socket failed. (sudo?)");
    return -1;
  }

  strncpy( ifDataIdx.ifr_name, argv[1], IFNAMSIZ-1 );

  result = ioctl( fd, SIOCGIFINDEX, &ifDataIdx );
  if (result < 0)
  {
    perror("Get Interface index failed.");
    close(fd);
    return -1;
  }

  strncpy( ifDataMac.ifr_name, argv[1], IFNAMSIZ-1 );

  result = ioctl( fd, SIOCGIFHWADDR, &ifDataMac );
  if (result < 0)
  {
    perror("Get Interface (own) MAC Address failed.");
    close(fd);
    return -1;
  }

  printf("Own MAC for %s (idx %d): %02x:%02x:%02x:%02x:%02x:%02x\n",
    ifDataIdx.ifr_name,
    ifDataIdx.ifr_ifindex,
    (unsigned char)ifDataMac.ifr_hwaddr.sa_data[0],
    (unsigned char)ifDataMac.ifr_hwaddr.sa_data[1],
    (unsigned char)ifDataMac.ifr_hwaddr.sa_data[2],
    (unsigned char)ifDataMac.ifr_hwaddr.sa_data[3],
    (unsigned char)ifDataMac.ifr_hwaddr.sa_data[4],
    (unsigned char)ifDataMac.ifr_hwaddr.sa_data[5]
  ); 

  address.sll_family = PF_PACKET;typedef struct MagicPacket
{
  struct ether_header eth;
  struct iphdr ip;
  struct icmphdr icmp;
  char data[56];
} MagicPacket;


  address.sll_ifindex = ifDataIdx.ifr_ifindex;
  address.sll_halen = ETH_ALEN;
  for (i=0; i<6; i++) 
    address.sll_addr[i] = mac[i];

  /* ether */
  for (i=0; i<6; i++) 
    packet.eth.ether_dhost[i] = mac[i];
  memcpy( packet.eth.ether_shost, ifDataMac.ifr_hwaddr.sa_data, 6 );
  packet.eth.ether_type = htons( ETHERTYPE_IP );

  /* ip */
  packet.ip.ihl = 5;
  packet.ip.version = 4;
  packet.ip.tos = 0;
  packet.ip.tot_len = htons(0x54);
  packet.ip.id = 0;
  packet.ip.frag_off = htons(0x4000);
  packet.ip.ttl = 0x40;
  packet.ip.protocol = IPPROTO_ICMP;
  packet.ip.check = htons(0x36a6); /* todo: calculate */
  packet.ip.saddr = 0x01010101; /* todo: better use own address */
  packet.ip.daddr = 0x01010101; /* todo: better use later address of device*/

  /* icmp: */
  packet.icmp.type = ICMP_ECHO;
  packet.icmp.code = 0;
  packet.icmp.checksum = htons( 0x1f5b ); /* not important, but keeps wireshark happy */
  packet.icmp.un.echo.id = 0;
  packet.icmp.un.echo.sequence = 0;

  for (i=0; i<56; i++)
   packet.data[i] = Magic[i % sizeof(Magic)];

  for (;;)
  {
    printf("Sending Packet to %s\n", argv[2]);
    result = sendto(fd, &packet, sizeof(packet), 0, (const struct sockaddr*)&address, sizeof(address));

    if (result < 0)
    {
      perror("sendto failed.");
      close(fd);
      return -1;
    }
    usleep(500000);
  }
}

It seems you have to use a 1000MBit LAN to get the packet received and the DHCP succeed.

The barebox loader doesn’t negotiate slower connections, at least on my machine.

Oh, and the funny typedef inside of main in the source above isn’t required, but doesn’t hurt.

Cut and Paste error…

I currently have no time to experiment myself.

Does your posting mean that barebox allows to establish a working connection before the box is booting uImage?

Do you see any possibility how one could boot from an USB stick attached to the USB-3 port?

Thank you!

I have already tried a bit more: You have to use GigaBit LAN, slower speeds will not connect.

Yes, this all happens before uImage is loaded.

I think it already happens before the disk is accessed at all.

No, I haven’t noticed anything to be able to boot from USB. If you can send the magic packet and get DHCP and TFTP to transport the startup.sh script, that could load the kernel from the stick. Maybe. But that doesn’t make sense, as you can already send your uImage also via tftp.

Important: This is not a “hidden” feature by WD, all is found in their GPL Sources. So you might search there if there is something with USB.

 I have test the tools, it works but haven’t sucessfull retrieve the startup.sh on a ftpd servers.

For others will be try there here the documentation  http://wiki.barebox.org/doku.php

If i successfull boot a new kernel i’ll post here :slight_smile:

Any news? If nobody has success this also is a sign of insufficient documentation!

Hmm, nothing really new here, beside that I was able to boot my own kernel.

I currently have a different problem: My GBIt LAN is not working with 2 Switches anymore, but only with the 3rd one.

I have no idea what I have done to get there. It worked with one of the switches perfectly. It seems the autonegotiation does not work anymore as it did before.

I tried several commands within barebox, and of course I opended the housing, but what is able to change that behaviour?

On the 3rd switch the box is working normally…

Anyway:

With that magic packet ping tool it is possible to start the steps to repair a completely bricked installation without opening the housing:

  1. Stop the normal boot-loader process with my tool (or something else that sends that magic ping)

  2. Answer the DHCP request with an Server that supports TFTP

  3. Deliver a “startup.sh” script via TFTP.

  4. That scripts loads a kernel via TFTP and boots that.

And that system is started. I expect that kernel can get its root fs via NFS.

2-4 can be done with dnsmasq.

To repair a disk it is not required to boot a kernel, maybe another barebox would work, too. One that is able to write to the disk and download the partition data from a PC. Maybe the current barebox supports that writing already?

But I don’t have that problem and I don’t plan to work on that, because currently I did not destroy my installation (at least on the disc)

Ah, just for your info:

My startup.sh:

timeout -c 10
addpart /dev/mem 4M@0x3008000(uImage)
tftp uImage /dev/mem.uImage
bootargs=“console=ttyS0,115200n8, init=/sbin/init”
bootargs=“$bootargs root=/dev/md0 raid=autodetect”
bootargs=“$bootargs rootfstype=ext3 rw noinitrd debug initcall_debug swapaccount=1 panic=3”
bootargs=“$bootargs mac_addr=$eth0.ethaddr”
bootargs=“$bootargs model=$model serial=$serial board_test=$board_test btn_status=$btn_status”
bootm /dev/mem.uImage

Most lines are copied from the normal boot scripts :slight_smile:

@baerle: Very interesting for me because I would like to install a kernel with slightly higher version. Do you think this is possible??

yes, I think it is possible. Booting a kernel that just comes via LAN should not change anything.

And if that kernel is working (and helps for the problem you try to solve), it shouldn’t be a problem to put it on the disk.

And if that fails, you can still boot a working kernel via LAN again and restore the old kernel on the disk.

My main reason to open the box (and search for the serial connection) was the same: To try a slightly different kernel.

I would say it helps a bit to have the serial connection when you try a new kernel, as you get the info if it does not work.

Currently I don’t have the time to work on that.

But if you create a newer kernel: Please tell us what you did!