Ticket #2897: nftp.2.c

File nftp.2.c, 9.0 KB (added by wilmer, 9 years ago)

Improved version of nftp.c, works with at least v4 routers.

Line 
1/*****************************************************************************\
2*                                                                             *
3*  nftp                                                                       *
4*                                                                             *
5*  Upload a firmware image to a bricked Netgear router using raw Ethernet     *
6*  frames.                                                                    *
7*                                                                             *
8*  Only tested with a DG834Gv4. Don't blame me if this breaks your router!    *
9*                                                                             *
10*  First version written by matteo (aka rootkit). I tried upslug2 but it      *
11*  didn't work and eventually uploaded something that still didn't make the   *
12*  router boot.                                                               *
13*                                                                             *
14*  After looking at a packet dump of a proper upgrade done using the Windows  *
15*  tool, I figured enhancing this program would be easier than trying to      *
16*  understand how upslug2 is supposed to work. I don't like C++.              *
17*                                                                             *
18*  This code isn't pretty, but I hacked it together quickly to get the job    *
19*  done. Hope it'll help someone.                                             *
20*                                                                             *
21*  Wilmer van der Gaast. <wilmer@gaast.net>                                   *
22*                                                                             *
23\*****************************************************************************/
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <sys/ioctl.h>
29#include <sys/socket.h>
30#include <linux/if_packet.h>
31#include <linux/if_ether.h>
32#include <linux/if_arp.h>
33#include <arpa/inet.h>
34
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38#include <unistd.h>
39                     
40#define NFTP_PROBE_LEN          0x40
41#define NFTP_MAX_PKT_LEN        1600
42#define NFTP_PROBE_RESP_LEN     0x66
43#define ETH_P_NFTP              0x8888
44
45#define NFTP_BLOCK_SIZE         1024
46#define IMG_VERIFY_BUF          65536
47#define IMG_VERIFY_STRING       "sercomm"
48
49typedef enum {
50        NFTP_TYPE_HWINFO = 0,
51        NFTP_TYPE_UPGRADESTART = 1,
52        NFTP_TYPE_UPGRADEDATA = 2,
53        NFTP_TYPE_REBOOT = 3,
54        NFTP_TYPE_UPGRADEVERIFY = 4,
55} nftp_type_t;
56
57#define DEBUG
58
59#ifdef DEBUG
60        #define D(x, ...) fprintf(stderr, x"\n", __VA_ARGS__)
61#else
62        #define D(...)
63#endif
64
65void usage(char *arg0)
66{
67        fprintf(stderr, "Usage: %s -u/-v iface file.img\n"
68                        "Example:\n\t %s -u eth0 firmware.img\n", arg0, arg0);
69        exit(1);
70}
71
72int sockfd;
73unsigned char src_mac[ETH_ALEN];
74unsigned char dst_mac[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
75struct sockaddr_ll socket_address;
76unsigned char pkt_buffer[NFTP_MAX_PKT_LEN];
77unsigned char *etherhead = pkt_buffer;
78unsigned char *data = pkt_buffer + 14;
79uint16_t *nftp_type = (uint16_t*) (pkt_buffer + 14);
80uint16_t *nftp_sequence = (uint16_t*) (pkt_buffer + 16);
81uint16_t *nftp_offset = (uint16_t*) (pkt_buffer + 18);
82uint16_t *nftp_chunk = (uint16_t*) (pkt_buffer + 20);
83uint16_t *nftp_payload_len = (uint16_t*) (pkt_buffer + 22);
84unsigned char *nftp_payload = pkt_buffer + 24;
85int send_pkt_len;
86int recv_pkt_len;
87
88int nftp_send()
89{
90        int st;
91
92        /*set the frame header*/
93        memcpy((void*)pkt_buffer, (void*)dst_mac, ETH_ALEN);
94        memcpy((void*)(pkt_buffer+ETH_ALEN), (void*)src_mac, ETH_ALEN);
95        ((struct ethhdr*)etherhead)->h_proto = ETH_P_NFTP;
96
97        socket_address.sll_halen = ETH_ALEN;
98        memcpy(socket_address.sll_addr, dst_mac, ETH_ALEN);
99
100        (*nftp_sequence)++;
101        send_pkt_len = (*nftp_payload_len) + 24;
102
103        st = sendto(sockfd, pkt_buffer, send_pkt_len, 0,
104                (struct sockaddr*)&socket_address, sizeof(socket_address));
105
106        if (st == -1) {
107                perror("sendto");
108                /* Rules of proper programming don't apply in a hack like this. :-P */
109                exit(1);
110        }
111}
112
113int nftp_recv()
114{
115        uint16_t st;
116       
117        do {
118                recv_pkt_len = recvfrom(sockfd, pkt_buffer, NFTP_PROBE_RESP_LEN, 0, NULL, NULL);
119                if (recv_pkt_len == -1) {
120                        perror("recvfrom");
121                        return 1;
122                }
123        } while (((struct ethhdr*)etherhead)->h_proto != ETH_P_NFTP);
124       
125        if (*nftp_payload_len == 2) {
126                st = *(uint16_t*)(nftp_payload);
127        } else {
128                st = 0;
129        }
130       
131        return st;
132}
133
134int nftp_sendrecv()
135{
136        uint16_t sequence;
137        int st;
138       
139        nftp_send();
140        sequence = *nftp_sequence;
141       
142        while (1) {
143                st = nftp_recv();
144               
145                if (*nftp_sequence != sequence) {
146                        D("Received unexpected packet seq=%d (expected %d)",
147                          *nftp_sequence, sequence);
148                        continue;
149                } else if (st != 0) {
150                        fprintf(stderr, "Received non-0 response from router, aborting.\n");
151                        exit(1);
152                }
153               
154                return st;
155        }
156}
157
158int nftp_sendfile(int imgfd, int imgsize, char *status)
159{
160        int imgoffset;
161       
162        lseek(imgfd, 0, SEEK_SET);
163        *nftp_chunk = imgoffset = 0;
164        while (read(imgfd, nftp_payload, NFTP_BLOCK_SIZE) == NFTP_BLOCK_SIZE) {
165                *nftp_payload_len = NFTP_BLOCK_SIZE;
166                nftp_sendrecv();
167                *nftp_chunk += (NFTP_BLOCK_SIZE >> 4);
168                imgoffset += NFTP_BLOCK_SIZE;
169                fprintf( stderr, "\r%s: %10d/%d bytes",status, imgoffset, imgsize );
170        }
171}
172
173int main(int argc, char *argv[])
174{
175        int send_result = 0, res, imgfd, upgrade = 0;
176        struct stat imginfo;
177        struct ifreq iface;
178        char *buf, *s;
179
180        if(argc < 3)
181                usage(argv[0]);
182
183        if (strcmp(argv[1], "-u") == 0) {
184                upgrade = 1;
185        } else if (strcmp(argv[1], "-v") != 0) {
186                usage(argv[0]);
187        }
188
189        /* Initialize the raw socket stuff. Most of this can be reused during
190           the whole session. */
191        sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
192        if (sockfd == -1) {
193                if(geteuid() != 0) {
194                        fprintf(stderr, "You should probably run this program as root.\n");
195                }
196                perror("socket");
197                return 1;
198        }
199        seteuid(getuid());
200        strncpy(iface.ifr_name, argv[2], IFNAMSIZ);
201
202        imgfd = open(argv[3], O_RDONLY);
203        fstat(imgfd, &imginfo);
204        if (imginfo.st_size % NFTP_BLOCK_SIZE) {
205                fprintf(stderr, "File size should be a multiple of %d.\n", NFTP_BLOCK_SIZE);
206                return 1;
207        }
208
209        res = ioctl(sockfd, SIOCGIFHWADDR, &iface);
210        if(res < 0){
211                perror("ioctl");
212                exit(1);
213        }
214
215        /*our MAC address*/
216        memcpy(src_mac, iface.ifr_hwaddr.sa_data, ETH_ALEN);
217        D("Sending frame on %s (%x:%x:%x:%x:%x:%x)", iface.ifr_name,
218          src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5]);
219
220        /*RAW communication*/
221        socket_address.sll_family   = PF_PACKET;
222        /*we don't use a protocoll above ethernet layer
223        *   ->just use anything here*/
224        socket_address.sll_protocol = htons(ETH_P_NFTP);
225
226        /*index of the network device
227        * see full code later how to retrieve it*/
228        res = ioctl(sockfd, SIOCGIFINDEX, &iface);
229        if(res < 0){
230                perror("ioctl");
231                exit(1);
232        }
233        socket_address.sll_ifindex  =iface.ifr_ifindex;
234
235        /* ARP hardware identifier is ethernet */
236        socket_address.sll_hatype   = ARPHRD_ETHER;
237
238        /* target is another host */
239        socket_address.sll_pkttype  = PACKET_OTHERHOST;
240
241        *nftp_type = NFTP_TYPE_HWINFO;
242        send_pkt_len = NFTP_PROBE_LEN;
243
244        nftp_send();
245        nftp_recv();
246
247        /* Now we know where to talk to, stop broadcasting! */
248        memcpy(dst_mac, pkt_buffer + ETH_ALEN, ETH_ALEN);
249        D("Found a router at %x:%x:%x:%x:%x:%x", dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);
250        D("Router is a %s", pkt_buffer + 0x1C);
251        D("Current version is %x%x", pkt_buffer[0x4A], pkt_buffer[0x4B]);
252        D("Max upgrade size is %d kb", (pkt_buffer[0x16] | (pkt_buffer[0x17] << 8) | (pkt_buffer[0x18] << 16)) - 20);
253
254        /* Some kind of verification of the firmware image. This really is
255           just some guess work based on what I saw in upslug2 and the images
256           for my router (not compatible with upslug2). */
257        buf = malloc(IMG_VERIFY_BUF);
258        lseek(imgfd, -IMG_VERIFY_BUF, SEEK_END);
259        read(imgfd, buf, IMG_VERIFY_BUF);
260        s = (char*) memmem(buf, IMG_VERIFY_BUF, nftp_payload, (size_t) (*nftp_payload_len));
261        if (s == NULL ||
262            strncasecmp(s - strlen(IMG_VERIFY_STRING), IMG_VERIFY_STRING, strlen(IMG_VERIFY_STRING)) != 0 ||
263            strncasecmp(s + *nftp_payload_len, IMG_VERIFY_STRING, strlen(IMG_VERIFY_STRING)) != 0) {
264                fprintf(stderr, "Could not find \"magic hardware header\" in this image.\n"
265                                "Uploading this is not recommended.\n");
266                return 1;
267        } else {
268                printf("The file looks suitable for this router (but don't trust this check blindly!)\n");
269                printf("If everything above looks good, press Enter now.\n");
270                read(0, buf, 1);
271        }
272       
273        *nftp_type = NFTP_TYPE_UPGRADESTART;
274        *nftp_offset = 0;
275        *nftp_chunk = 0;
276        *nftp_payload_len = 0;
277
278        nftp_sendrecv();
279
280        if (upgrade) {
281                /* When the first packet comes in, the router will start
282                   erasing flash before it sends an ACK. Keep the user
283                   updated in the meantime. */
284                printf("Erasing flash, this will take around ten seconds...\n");
285                *nftp_type = NFTP_TYPE_UPGRADEDATA;
286                nftp_sendfile(imgfd, imginfo.st_size, "Upgrading");
287                printf("\nUpload completed, will now verify:\n");
288        }
289       
290        *nftp_type = NFTP_TYPE_UPGRADEVERIFY;
291        nftp_sendfile(imgfd, imginfo.st_size, "Verifying");
292
293        if (upgrade) {
294                *nftp_type = NFTP_TYPE_REBOOT;
295                *nftp_payload_len = 0;
296                nftp_sendrecv();
297        }
298
299        printf("\nFirmware updated/verified successfully!\n");
300       
301        return 0;
302}