This source file includes following definitions.
- new_list
- free_list
- new_node
- l_search
- l_insert
- l_remove
- addr_hash_new
- addr_hash_free
- addr_hash_get
- addr_hash_add
- usage
- arm_op_type_name
- describe_insn_ops
- describe_insn_groups
- describe_str
- describe_const_op
- describe_prop_call
- describe_simple_func
- do_dis_branch
- do_dis_call
- do_dis_insn
- do_adr_label
- do_tbb_data
- do_tbh_data
- do_tbx_pass1
- do_tbx_data
- do_dis_range
- main
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <inttypes.h>
5 #include <sys/stat.h>
6
7 #include <capstone.h>
8
9 #include "stubs_load.h"
10 #include "firmware_load_ng.h"
11
12
13
14 typedef unsigned int t_address;
15 typedef unsigned int t_value;
16
17
18
19
20
21 struct lnode {
22 struct lnode *next;
23 t_address address;
24 t_value data ;
25 };
26
27 struct llist {
28 struct lnode *head;
29 int size;
30 };
31
32
33
34
35 struct llist * new_list()
36 {
37 struct llist *lst;
38
39 lst = (struct llist *) malloc(sizeof(struct llist));
40 if (lst == NULL) {
41 printf("\n **new_list() : malloc error");
42 return NULL;
43 }
44 lst->head = 0;
45 lst->size = 0;
46
47 return lst;
48 }
49
50 void free_list(struct llist *lst)
51 {
52 if (lst)
53 {
54 struct lnode *p, *n;
55 p = lst->head;
56 while (p)
57 {
58 n = p->next;
59 free(p);
60 p = n;
61 }
62 free(lst);
63 }
64 }
65
66
67
68
69 struct lnode * new_node(t_address address, t_value data) {
70 struct lnode *node;
71
72 node = (struct lnode *) malloc (sizeof (struct lnode));
73 if (node == NULL) {
74 printf("\n **new_node() : malloc error");
75 return NULL;
76 }
77 node->address = address;
78 node->data = data;
79 node->next = 0;
80
81 return node;
82 }
83
84
85
86
87 struct lnode * l_search(struct llist *ls, t_address address) {
88 struct lnode *node;
89
90 node = ls->head;
91 while ( node != NULL && node->address != address ) {
92 node = node->next ;
93 }
94 if (node == NULL) {
95 return 0;
96 }
97
98 return node;
99 }
100
101
102
103
104 int l_insert(struct llist *ls, t_address address, t_value data)
105 {
106 struct lnode *node;
107
108 if( l_search(ls, address)) return -1 ;
109 node = new_node(address, data);
110 if (node == NULL) return 0;
111 node->next = ls->head;
112 ls->head = node;
113 (ls->size)++;
114
115 return 1;
116 }
117
118 void l_remove(struct llist *ls, t_address addr)
119 {
120 if (ls)
121 {
122 struct lnode *p, *l;
123 l = 0;
124 p = ls->head;
125 while (p)
126 {
127 if (p->address == addr)
128 {
129 if (l)
130 l->next = p->next;
131 else
132 ls->head = p->next;
133 (ls->size)--;
134 return;
135 }
136 l = p;
137 p = p->next;
138 }
139 }
140 }
141
142 #define ADDR_HASH_BITS 20
143 #define ADDR_HASH_BUCKETS (1<<ADDR_HASH_BITS)
144 #define ADDR_HASH_MASK (ADDR_HASH_BUCKETS-1)
145 #define ADDR_HASH(addr) (((unsigned)(addr) >> 2) & ADDR_HASH_MASK)
146 struct llist ** addr_hash_new() {
147 struct llist **addr_hash = malloc(ADDR_HASH_BUCKETS*sizeof(struct llist *));
148 if(!addr_hash) {
149 fprintf(stderr,"addr_hash_now: malloc failed\n");
150 exit(1);
151 }
152 memset(addr_hash,0,ADDR_HASH_BUCKETS*sizeof(struct llist *));
153 return addr_hash;
154 }
155
156 void addr_hash_free(struct llist **addr_hash) {
157 int i;
158 for(i=0;i<ADDR_HASH_BUCKETS; i++) {
159 if(addr_hash[i]) {
160 free_list(addr_hash[i]);
161 }
162 }
163 free(addr_hash);
164 }
165 struct lnode * addr_hash_get(struct llist **addr_hash,t_address addr) {
166 unsigned key = ADDR_HASH(addr);
167 if(!addr_hash[key]) {
168 return NULL;
169 }
170 return l_search(addr_hash[key],addr);
171 }
172
173 void addr_hash_add(struct llist **addr_hash,t_address addr) {
174 unsigned key = ADDR_HASH(addr);
175 if(!addr_hash[key]) {
176 addr_hash[key] = new_list();
177 }
178 l_insert(addr_hash[key],addr,0);
179 }
180
181
182
183 static t_address set_prop;
184 static t_address get_prop;
185
186 void usage(void) {
187 fprintf(stderr,"usage capdis [options] <file> <load address>\n"
188 "options:\n"
189 " -c=<count> disassemble at most <count> instructions\n"
190 " -s=<address|stub name> start disassembling at <address>. LSB controls ARM/thumb mode\n"
191 " -e=<address> stop disassembling at <address>\n"
192 " -o=<offset> start disassembling at <offset>. LSB controls ARM/thumb mode\n"
193 " -f=<chdk|objdump> format as CHDK inline ASM, or similar to objdump (default clean ASM)\n"
194 " -armv5 make firmware_load treat firmware as armv5\n"
195 " -stubs[=dir] load / use stubs from dir (default .) for names\n"
196 " -props=<n> load propset<n>.h and identify propcases in calls. Requires -stubs\n"
197 " -v increase verbosity\n"
198 " -d-const add details about pc relative constant LDRs\n"
199 " -d-bin print instruction hex dump\n"
200 " -d-addr print instruction addresses\n"
201 " -d-ops print instruction operand information\n"
202 " -d-groups print instruction group information\n"
203 " -d all of the -d-* above\n"
204 " -noloc don't generate loc_xxx: labels and B loc_xxx\n"
205 " -nosub don't generate BL sub_xxx\n"
206 " -noconst don't generate LDR Rd,=0xFOO\n"
207 " -nostr don't comments for string refs\n"
208 " -adrldr convert ADR Rd,#x and similar to LDR RD,=... (default with -f=chdk)\n"
209 " -noadrldr don't convert ADR Rd,#x and similar to LDR RD,=...\n"
210 " -nofwdata don't attempt to initialize firmware data ranges\n"
211 " -nosimplefunc don't comment calls / jumps that return immediately or imm constant\n"
212 " -jfw generate LDR PC,=0x... to return to firmware at end of disassembled range\n"
213 " -eret[=n] stop on Nth return-like instruction (if no count / end, sets count=500)\n"
214 );
215 exit(1);
216 }
217
218
219 static const char *arm_op_type_name(int op_type) {
220 switch(op_type) {
221 case ARM_OP_INVALID:
222 return "invalid";
223 case ARM_OP_REG:
224 return "reg";
225 case ARM_OP_IMM:
226 return "imm";
227 case ARM_OP_MEM:
228 return "mem";
229 case ARM_OP_FP:
230 return "fp";
231 case ARM_OP_CIMM:
232 return "cimm";
233 case ARM_OP_PIMM:
234 return "pimm";
235 case ARM_OP_SETEND:
236 return "setend";
237 case ARM_OP_SYSREG:
238 return "sysreg";
239 }
240 return "unk";
241 }
242
243 const char *comment_start = ";";
244
245 static void describe_insn_ops(csh handle, cs_insn *insn) {
246 printf("%s OPERANDS %d:\n",comment_start,insn->detail->arm.op_count);
247 int i;
248 for(i=0;i<insn->detail->arm.op_count;i++) {
249 printf("%s %d: %s",comment_start,i,arm_op_type_name(insn->detail->arm.operands[i].type));
250 switch(insn->detail->arm.operands[i].type) {
251 case ARM_OP_IMM:
252 case ARM_OP_PIMM:
253 case ARM_OP_CIMM:
254 printf("=0x%x",insn->detail->arm.operands[i].imm);
255 break;
256 case ARM_OP_MEM: {
257 const char *reg=cs_reg_name(handle,insn->detail->arm.operands[i].mem.base);
258 if(reg) {
259 printf(" base=%s",reg);
260 }
261 reg=cs_reg_name(handle,insn->detail->arm.operands[i].mem.index);
262 if(reg) {
263 printf(" index=%s",reg);
264 }
265 if(insn->detail->arm.operands[i].mem.disp) {
266 printf(" scale=%d disp=0x%08x",
267 insn->detail->arm.operands[i].mem.scale,
268 insn->detail->arm.operands[i].mem.disp);
269 }
270 break;
271 }
272 case ARM_OP_REG:
273 printf(" %s",cs_reg_name(handle,insn->detail->arm.operands[i].reg));
274 break;
275 default:
276 break;
277 }
278 printf("\n");
279 }
280 }
281 static void describe_insn_groups(csh handle, cs_insn *insn) {
282 int i;
283 printf("%s GROUPS %d:",comment_start,insn->detail->groups_count);
284 for(i=0;i<insn->detail->groups_count;i++) {
285 if(i>0) {
286 printf(",");
287 }
288 printf("%s",cs_group_name(handle,insn->detail->groups[i]));
289 }
290 printf("\n");
291 }
292
293 #define COMMENT_LEN 255
294
295
296
297
298 static void describe_str(firmware *fw, char *comment, uint32_t adr)
299 {
300 int plevel=1;
301 char *s=(char *)adr2ptr_with_data(fw,adr);
302 if(!s) {
303
304 return;
305 }
306 if(!isASCIIstring(fw,adr)) {
307
308
309 uint32_t adr2=*(uint32_t *)s;
310
311 s=(char *)adr2ptr_with_data(fw,adr2);
312 if(!s) {
313
314 return;
315 }
316 if(!isASCIIstring(fw,adr2)) {
317
318 return;
319 }
320 plevel++;
321 }
322 int l=strlen(comment);
323
324
325 int r=COMMENT_LEN - (l + 4 + plevel);
326 if(r < 1) {
327 return;
328 }
329 char *p=comment+l;
330 *p++=' ';
331 int i;
332 for(i=0;i<plevel;i++) {
333 *p++='*';
334 }
335 *p++='"';
336 char *e;
337 int trunc=0;
338 if((int)strlen(s) < r) {
339 e=p+strlen(s);
340 } else {
341
342 if(r <= 3) {
343 *p++='"';
344 *p++=0;
345 return;
346 }
347 e=p+r - 3;
348 trunc=1;
349 }
350 while(p < e) {
351 if(*s == '\r') {
352 *p++='\\';
353 *p++='r';
354 } else if(*s == '\n') {
355 *p++='\\';
356 *p++='n';
357 } else {
358 *p++=*s;
359 }
360 s++;
361 }
362 if(trunc) {
363 strcpy(p,"...");
364 p+=3;
365 }
366 *p++='"';
367 *p++=0;
368 }
369
370 #define DIS_OPT_LABELS 0x00000001
371 #define DIS_OPT_SUBS 0x00000002
372 #define DIS_OPT_CONSTS 0x00000004
373 #define DIS_OPT_FMT_CHDK 0x00000008
374 #define DIS_OPT_FMT_OBJDUMP 0x00000010
375 #define DIS_OPT_ADR_LDR 0x00000020
376 #define DIS_OPT_STR 0x00000040
377 #define DIS_OPT_STUBS 0x00000080
378 #define DIS_OPT_STUBS_LABEL 0x00000100
379 #define DIS_OPT_PROPS 0x00000200
380 #define DIS_OPT_JMP_BACK 0x00000400
381 #define DIS_OPT_END_RET 0x00000800
382 #define DIS_OPT_SIMPLE_FUNCS 0x00001000
383
384 #define DIS_OPT_DETAIL_GROUP 0x00010000
385 #define DIS_OPT_DETAIL_OP 0x00020000
386 #define DIS_OPT_DETAIL_ADDR 0x00040000
387 #define DIS_OPT_DETAIL_BIN 0x00080000
388 #define DIS_OPT_DETAIL_CONST 0x00100000
389 #define DIS_OPT_DETAIL_ALL (DIS_OPT_DETAIL_GROUP\
390 |DIS_OPT_DETAIL_OP\
391 |DIS_OPT_DETAIL_ADDR\
392 |DIS_OPT_DETAIL_BIN\
393 |DIS_OPT_DETAIL_CONST)
394
395
396 void describe_const_op(firmware *fw, unsigned dis_opts, char *comment, uint32_t adr)
397 {
398 osig* ostub=NULL;
399 if(dis_opts & DIS_OPT_STUBS) {
400 ostub = find_sig_val(fw->sv->stubs,adr);
401 if(!ostub) {
402 uint32_t *p=(uint32_t *)adr2ptr(fw,adr);
403 if(p) {
404 ostub = find_sig_val(fw->sv->stubs,*p);
405 }
406 }
407
408 if(ostub && ostub->val) {
409
410 strcat(comment," ");
411 strcat(comment,ostub->nm);
412
413 return;
414 }
415 }
416 if(dis_opts & DIS_OPT_STR) {
417 describe_str(fw,comment,adr);
418 }
419 }
420
421 void describe_prop_call(firmware *fw,iter_state_t *is, unsigned dis_opts, char *comment, uint32_t target)
422 {
423 if(!(dis_opts & DIS_OPT_PROPS) || (target != get_prop && target != set_prop)) {
424 return;
425 }
426
427 uint32_t regs[4];
428
429 if((get_call_const_args(fw,is,6,regs)&1)!=1) {
430 return;
431 }
432 osig* ostub = find_sig_val(fw->sv->propcases,regs[0]);
433 if(ostub && ostub->val) {
434 sprintf(comment+strlen(comment)," %s (%d)",ostub->nm,ostub->val);
435 } else {
436 sprintf(comment+strlen(comment)," (%d)",regs[0]);
437 }
438 }
439
440 void describe_simple_func(firmware *fw, unsigned dis_opts, char *comment, uint32_t target)
441 {
442 if(!(dis_opts & DIS_OPT_SIMPLE_FUNCS)) {
443 return;
444 }
445 simple_func_desc_t info;
446 if(!check_simple_func(fw, target, MATCH_SIMPLE_FUNC_ANY, &info)) {
447 return;
448 }
449 if(info.ftype == MATCH_SIMPLE_FUNC_NULLSUB) {
450 strcat(comment," return");
451 } else if (info.ftype == MATCH_SIMPLE_FUNC_IMM) {
452 sprintf(comment+strlen(comment)," return %#x",info.retval);
453 }
454 }
455
456
457
458 int do_dis_branch(firmware *fw, iter_state_t *is, unsigned dis_opts, char *ops, char *comment)
459 {
460 uint32_t target = B_target(fw,is->insn);
461 char op_pfx[8]="";
462 if(!target) {
463 target = CBx_target(fw,is->insn);
464 if(!target) {
465 return 0;
466 }
467 sprintf(op_pfx,"%s, ",cs_reg_name(is->cs_handle,is->insn->detail->arm.operands[0].reg));
468 }
469 osig* ostub=NULL;
470 char *subname=NULL;
471 uint32_t j_target=0;
472 if(dis_opts & DIS_OPT_STUBS) {
473
474 ostub = find_sig_val(fw->sv->stubs,target|is->thumb);
475 if(ostub) {
476 if(ostub->is_comment) {
477 strcat(comment,ostub->nm);
478 } else {
479 subname=ostub->nm;
480 }
481 } else {
482
483 if(fw_disasm_iter_single(fw,target|is->thumb)) {
484 j_target=get_direct_jump_target(fw,fw->is);
485 if(j_target) {
486 ostub = find_sig_val(fw->sv->stubs,j_target);
487 if(ostub) {
488 strcat(comment,"-> ");
489 strcat(comment,ostub->nm);
490 }
491 }
492 }
493 }
494 }
495 if(dis_opts & DIS_OPT_LABELS) {
496 if(subname) {
497 if(dis_opts & DIS_OPT_FMT_CHDK) {
498 sprintf(ops,"%s_%s",op_pfx,subname);
499 } else {
500 sprintf(ops,"%s%s",op_pfx,subname);
501 }
502 } else {
503 sprintf(ops,"%sloc_%08x",op_pfx,target);
504 }
505 } else if (subname) {
506 strcat(comment,subname);
507 }
508 uint32_t desc_adr = (j_target)?j_target:target;
509 describe_prop_call(fw,is,dis_opts,comment,desc_adr | is->thumb);
510 describe_simple_func(fw,dis_opts,comment,desc_adr | is->thumb);
511 return 1;
512 }
513
514
515 int do_dis_call(firmware *fw, iter_state_t *is, unsigned dis_opts, char *ops, char *comment)
516 {
517 if(!((is->insn->id == ARM_INS_BL || is->insn->id == ARM_INS_BLX)
518 && is->insn->detail->arm.operands[0].type == ARM_OP_IMM)) {
519 return 0;
520 }
521
522 uint32_t target = get_branch_call_insn_target(fw,is);
523 osig* ostub=NULL;
524 char *subname=NULL;
525 uint32_t j_target=0;
526 if(dis_opts & DIS_OPT_STUBS) {
527 ostub = find_sig_val(fw->sv->stubs,target);
528 if(ostub) {
529 if(ostub->is_comment) {
530 strcat(comment,ostub->nm);
531 } else {
532 subname=ostub->nm;
533 }
534 } else {
535
536 if(fw_disasm_iter_single(fw,target)) {
537 j_target=get_direct_jump_target(fw,fw->is);
538 if(j_target) {
539 ostub = find_sig_val(fw->sv->stubs,j_target);
540 if(ostub) {
541
542 strcat(comment,"-> ");
543 strcat(comment,ostub->nm);
544 }
545 }
546 }
547 }
548 }
549 if(dis_opts & DIS_OPT_SUBS) {
550 if(subname) {
551 if(dis_opts & DIS_OPT_FMT_CHDK) {
552 sprintf(ops,"_%s",subname);
553 } else {
554 strcpy(ops,subname);
555 }
556 } else {
557 sprintf(ops,"sub_%08x",ADR_CLEAR_THUMB(target));
558 }
559 } else if (subname) {
560 strcat(comment,subname);
561 }
562 uint32_t desc_adr = (j_target)?j_target:target;
563 describe_prop_call(fw,is,dis_opts,comment,desc_adr);
564 describe_simple_func(fw,dis_opts,comment,desc_adr);
565 return 1;
566 }
567
568 static void do_dis_insn(
569 firmware *fw,
570 iter_state_t *is,
571 unsigned dis_opts,
572 char *mnem,
573 char *ops,
574 char *comment,
575 tbx_info_t *ti) {
576
577 cs_insn *insn=is->insn;
578
579
580 strcpy(mnem,insn->mnemonic);
581 strcpy(ops,insn->op_str);
582 comment[0]=0;
583
584 if(do_dis_branch(fw,is,dis_opts,ops,comment)) {
585 return;
586 }
587 if(do_dis_call(fw,is,dis_opts,ops,comment)) {
588 return;
589 }
590 if((dis_opts & (DIS_OPT_CONSTS|DIS_OPT_DETAIL_CONST)) && isLDR_PC(insn)) {
591
592 uint32_t ad=LDR_PC2adr(fw,insn);
593 uint32_t *pv=(uint32_t *)adr2ptr(fw,ad);
594
595 if(pv) {
596 if(dis_opts & DIS_OPT_CONSTS) {
597 sprintf(ops,"%s, =0x%08x",
598 cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg),
599 *pv);
600 if(dis_opts & DIS_OPT_DETAIL_CONST) {
601
602 sprintf(comment,"[pc, #%d] (0x%08x)",insn->detail->arm.operands[1].mem.disp,ad);
603 }
604 } else if(dis_opts & DIS_OPT_DETAIL_CONST) {
605
606 sprintf(comment,"0x%08x: (%08x)",ad,*pv);
607 }
608 describe_const_op(fw,dis_opts,comment,ad);
609 } else {
610 sprintf(comment,"WARNING didn't convert PC rel to constant!");
611 }
612 } else if((dis_opts & (DIS_OPT_CONSTS|DIS_OPT_DETAIL_CONST)) && isADRx(insn)) {
613 unsigned ad=ADRx2adr(fw,insn);
614 uint32_t *pv=(uint32_t *)adr2ptr(fw,ad);
615 if(pv) {
616 if(dis_opts & DIS_OPT_ADR_LDR) {
617 strcpy(mnem,"ldr");
618 sprintf(ops,"%s, =0x%08x",
619 cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg),
620 ad);
621 if(dis_opts & DIS_OPT_DETAIL_CONST) {
622
623 if(insn->id == ARM_INS_ADR) {
624 sprintf(comment,"adr %s, #%x (0x%08x)",
625 cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg),
626 insn->detail->arm.operands[1].imm,
627 *pv);
628 } else {
629 sprintf(comment,"%s %s, pc, #%x (0x%08x)",
630 insn->mnemonic,
631 cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg),
632 insn->detail->arm.operands[2].imm,
633 *pv);
634 }
635 }
636 } else {
637 if(insn->id == ARM_INS_ADR) {
638 if(dis_opts & DIS_OPT_FMT_OBJDUMP) {
639 strcpy(mnem,"add");
640 sprintf(ops,"%s, pc, #%d",
641 cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg),
642 insn->detail->arm.operands[1].imm);
643 }
644 }
645 if(dis_opts & DIS_OPT_DETAIL_CONST) {
646
647 sprintf(comment,"0x%08x: (%08x)",ad,*pv);
648 }
649 }
650 describe_const_op(fw,dis_opts,comment,ad);
651 } else {
652 sprintf(comment,"WARNING didn't convert ADR/ADD/SUB Rd, PC, #x to constant!");
653 }
654 } else if(get_TBx_PC_info(fw,is,ti)) {
655 sprintf(comment+strlen(comment),
656 "(jumptable r%d %d elements)",
657 insn->detail->arm.operands[0].mem.index - ARM_REG_R0,
658 ti->count);
659 }
660
661 }
662
663 void do_adr_label(firmware *fw, struct llist **branch_list, iter_state_t *is, unsigned dis_opts)
664 {
665
666 uint32_t adr=is->insn->address;
667 osig* ostub = NULL;
668
669 if(dis_opts & DIS_OPT_STUBS_LABEL) {
670
671 ostub = find_sig_val(fw->sv->stubs,adr|is->thumb);
672
673 if(ostub) {
674 printf("%s %s 0x%08x\n",comment_start,ostub->nm,ostub->val);
675 }
676 }
677 if(dis_opts & DIS_OPT_LABELS) {
678 struct lnode *label = addr_hash_get(branch_list,adr);
679 if(label) {
680 if(dis_opts & DIS_OPT_FMT_CHDK) {
681 printf("\"");
682 }
683 printf("loc_%08x:", label->address);
684 if(dis_opts & DIS_OPT_FMT_CHDK) {
685 printf("\\n\"");
686 }
687 printf("\n");
688 }
689 }
690 }
691
692 static void do_tbb_data(firmware *fw, iter_state_t *is, unsigned dis_opts, tbx_info_t *ti)
693 {
694 uint32_t adr=ti->start;
695 if(dis_opts & DIS_OPT_FMT_CHDK) {
696 printf("\"branchtable_%08x:\\n\"\n",adr);
697 } else {
698 printf("branchtable_%08x:\n",adr);
699 }
700 uint32_t i=0;
701 while(i < ti->count) {
702 uint8_t *p=adr2ptr(fw,adr);
703 if(!p) {
704 fprintf(stderr,"do_tbb_data: jumptable outside of valid address range at 0x%08x\n",adr);
705 break;
706 }
707 uint32_t target = ti->start+2**p;
708
709 if(target <= adr) {
710 if(dis_opts & DIS_OPT_FMT_CHDK) {
711 printf("\" .byte 0x%02x\\n\" %s invalid juptable data?\n",*p,comment_start);
712 } else {
713 printf(" .byte 0x%02x ; invalid juptable data?\n",*p);
714 }
715 i++;
716 adr++;
717 continue;
718 }
719 if(dis_opts & DIS_OPT_FMT_CHDK) {
720 printf("\" .byte((loc_%08x - branchtable_%08x) / 2)\\n\" %s (case %d)\n",target,ti->start,comment_start,i);
721 } else {
722 printf(" .byte 0x%02x %s jump to loc_%08x (case %d)\n",*p,comment_start,target,i);
723 }
724 adr++;
725 i++;
726 }
727 if(dis_opts & DIS_OPT_FMT_CHDK) {
728
729
730
731 printf("\".align 1\\n\"\n");
732 }
733
734 if(adr & 1) {
735 uint8_t *p=adr2ptr(fw,adr);
736 if(p) {
737 if(!(dis_opts & DIS_OPT_FMT_CHDK)) {
738 printf(" .byte 0x%02x\n",*p);
739 }
740 } else {
741 fprintf(stderr,"do_tbb_data: jumptable outside of valid address range at 0x%08x\n",adr);
742 }
743 adr++;
744 }
745
746 if(!disasm_iter_init(fw,is,adr | is->thumb)) {
747 fprintf(stderr,"do_tbb_data: disasm_iter_init failed\n");
748 }
749 }
750
751
752
753
754 static void do_tbh_data(firmware *fw, iter_state_t *is, unsigned dis_opts, tbx_info_t *ti)
755 {
756 uint32_t adr=ti->start;
757 if(dis_opts & DIS_OPT_FMT_CHDK) {
758 printf("\"branchtable_%08x:\\n\"\n",adr);
759 } else {
760 printf("branchtable_%08x:\n",adr);
761 }
762 uint32_t i=0;
763 while(i < ti->count) {
764 uint16_t *p=(uint16_t *)adr2ptr(fw,adr);
765 if(!p) {
766 fprintf(stderr,"do_tbh_data: jumptable outside of valid address range at 0x%08x\n",adr);
767 break;
768 }
769 uint32_t target = ti->start+2**p;
770
771 if(target <= adr) {
772 if(dis_opts & DIS_OPT_FMT_CHDK) {
773 printf("\" .short 0x%04x\\n\" %s invalid juptable data?\n",*p,comment_start);
774 } else {
775 printf(" .short 0x%04x ; invalid juptable data?\n",*p);
776 }
777 i++;
778 adr+=ti->bytes;
779 continue;
780 }
781 if(dis_opts & DIS_OPT_FMT_CHDK) {
782 printf("\" .short((loc_%08x - branchtable_%08x) / 2)\\n\" %s (case %d)\n",target,ti->start,comment_start,i);
783 } else {
784 printf(" .short 0x%04x %s jump to loc_%08x (case %d)\n",*p,comment_start,target,i);
785 }
786 adr+=ti->bytes;
787 i++;
788 }
789
790
791 if(!disasm_iter_init(fw,is,adr | is->thumb)) {
792 fprintf(stderr,"do_tbh_data: disasm_iter_init failed\n");
793 }
794 }
795
796 static void do_tbx_pass1(firmware *fw, iter_state_t *is, struct llist **branch_list, unsigned dis_opts, tbx_info_t *ti)
797 {
798 uint32_t adr=ti->start;
799 uint32_t i=0;
800 while(i < ti->count) {
801 uint8_t *p=adr2ptr(fw,adr);
802 if(!p) {
803 fprintf(stderr,"do_tbb_data: jumptable outside of valid address range at 0x%08x\n",adr);
804 break;
805 }
806 uint16_t off;
807 if(ti->bytes==1) {
808 off=(uint16_t)*p;
809 } else {
810 off=*(uint16_t *)p;
811 }
812 uint32_t target = ti->start+2*off;
813
814 if(target <= adr) {
815 adr++;
816 continue;
817 }
818 if(dis_opts & DIS_OPT_LABELS) {
819 addr_hash_add(branch_list,target);
820 }
821 adr+=ti->bytes;
822 i++;
823 }
824
825 if(adr & 1) {
826 adr++;
827 }
828
829 if(!disasm_iter_init(fw,is,adr | is->thumb)) {
830 fprintf(stderr,"do_tbb_data: disasm_iter_init failed\n");
831 }
832 }
833
834
835 static void do_tbx_data(firmware *fw, iter_state_t *is, unsigned dis_opts, tbx_info_t *ti)
836 {
837 if(ti->bytes==1) {
838 do_tbb_data(fw,is,dis_opts,ti);
839 } else {
840 do_tbh_data(fw,is,dis_opts,ti);
841 }
842 }
843
844 static void do_dis_range(firmware *fw,
845 unsigned dis_start,
846 unsigned dis_count,
847 unsigned dis_end,
848 unsigned dis_end_ret_count,
849 unsigned dis_opts)
850 {
851 iter_state_t *is=disasm_iter_new(fw,dis_start);
852 size_t count=0;
853 struct llist **branch_list = addr_hash_new();
854 tbx_info_t ti;
855
856
857 if(dis_opts & DIS_OPT_LABELS) {
858
859 while(count < dis_count && is->adr < dis_end) {
860 if(disasm_iter(fw,is)) {
861 uint32_t b_tgt=get_branch_call_insn_target(fw,is);
862 if(b_tgt) {
863
864 addr_hash_add(branch_list,ADR_CLEAR_THUMB(b_tgt));
865 } else if(get_TBx_PC_info(fw,is,&ti)) {
866
867
868 do_tbx_pass1(fw,is,branch_list,dis_opts,&ti);
869 }
870 } else {
871 if(!disasm_iter_init(fw,is,(is->adr+is->insn_min_size) | is->thumb)) {
872 fprintf(stderr,"do_dis_range: disasm_iter_init failed\n");
873 break;
874 }
875 }
876 count++;
877 }
878 }
879 count=0;
880 disasm_iter_init(fw,is,dis_start);
881 while(count < dis_count && is->adr < dis_end) {
882 if(disasm_iter(fw,is)) {
883 do_adr_label(fw,branch_list,is,dis_opts);
884 if(!(dis_opts & DIS_OPT_FMT_OBJDUMP)
885 && (dis_opts & (DIS_OPT_DETAIL_ADDR | DIS_OPT_DETAIL_BIN))) {
886 printf("%s",comment_start);
887 if(dis_opts & DIS_OPT_DETAIL_ADDR) {
888 printf(" 0x%"PRIx64"",is->insn->address);
889 }
890 if(dis_opts & DIS_OPT_DETAIL_BIN) {
891 int k;
892 for(k=0;k<is->insn->size;k++) {
893 printf(" %02x",is->insn->bytes[k]);
894 }
895 }
896 printf("\n");
897 }
898 if(dis_opts & DIS_OPT_DETAIL_OP) {
899 describe_insn_ops(is->cs_handle,is->insn);
900 }
901 if(dis_opts & DIS_OPT_DETAIL_GROUP) {
902 describe_insn_groups(is->cs_handle,is->insn);
903 }
904
905 char insn_mnemonic[32], insn_ops[160], comment[COMMENT_LEN+1];
906 ti.start=0;
907 do_dis_insn(fw,is,dis_opts,insn_mnemonic,insn_ops,comment,&ti);
908 if(dis_opts & DIS_OPT_FMT_CHDK) {
909 printf("\"");
910 }
911
912
913
914
915
916
917
918
919 if(dis_opts & DIS_OPT_FMT_OBJDUMP) {
920 if(dis_opts & DIS_OPT_DETAIL_ADDR) {
921 printf("%08"PRIx64": \t",is->insn->address);
922 }
923 if(dis_opts & DIS_OPT_DETAIL_BIN) {
924 if(is->insn->size == 2) {
925 printf("%02x%02x ",is->insn->bytes[1],is->insn->bytes[0]);
926 } else if(is->insn->size == 4) {
927 printf("%02x%02x %02x%02x",is->insn->bytes[1],is->insn->bytes[0],is->insn->bytes[3],is->insn->bytes[2]);
928 }
929 }
930 printf(" \t%s", insn_mnemonic);
931 if(strlen(insn_ops)) {
932 printf("\t%s",insn_ops);
933 }
934 if(strlen(comment)) {
935 printf("\t%s %s",comment_start,comment);
936 }
937 } else {
938 printf(" %-7s", insn_mnemonic);
939 if(strlen(insn_ops)) {
940 printf(" %s",insn_ops);
941 }
942 if(dis_opts & DIS_OPT_FMT_CHDK) {
943 printf("\\n\"");
944 }
945 if(strlen(comment)) {
946 printf(" %s %s",comment_start,comment);
947 }
948 }
949 printf("\n");
950 if(ti.start) {
951 do_tbx_data(fw,is,dis_opts,&ti);
952 }
953 if((dis_opts & DIS_OPT_END_RET) && isRETx(is->insn)) {
954 if(dis_end_ret_count > 1) {
955 dis_end_ret_count--;
956 } else {
957 count++;
958
959
960
961
962
963
964 break;
965 }
966 }
967 } else {
968 uint16_t *pv=(uint16_t *)adr2ptr(fw,is->adr);
969
970 if(pv) {
971 if(is->thumb) {
972 printf("%s %04x\n",comment_start,*pv);
973 } else {
974 printf("%s %04x %04x\n",comment_start,*pv,*(pv+1));
975 }
976 } else {
977 printf("%s invalid address %"PRIx64"\n",comment_start,is->adr);
978 }
979 if(!disasm_iter_init(fw,is,(is->adr+is->insn_min_size)|is->thumb)) {
980 fprintf(stderr,"do_dis_range: disasm_iter_init failed\n");
981 break;
982 }
983 }
984 count++;
985 }
986 if(dis_opts & DIS_OPT_JMP_BACK) {
987 if(dis_opts & DIS_OPT_FMT_CHDK) {
988 printf("\"");
989 }
990
991 printf(" ldr pc, =0x%"PRIx64,is->adr|is->thumb);
992 if(dis_opts & DIS_OPT_FMT_CHDK) {
993 printf("\\n\"");
994 }
995 printf(" %s Continue in firmware\n",comment_start);
996 }
997 addr_hash_free(branch_list);
998 }
999
1000 int main(int argc, char** argv)
1001 {
1002 char stubs_dir[256]="";
1003 char *dumpname=NULL;
1004 unsigned load_addr=0xFFFFFFFF;
1005 unsigned offset=0xFFFFFFFF;
1006 unsigned dis_start=0;
1007 const char *dis_start_fn=NULL;
1008 unsigned dis_end=0;
1009 unsigned dis_end_ret_count=0;
1010 unsigned dis_count=0;
1011 int do_fw_data_init=1;
1012 int verbose=0;
1013 unsigned dis_opts=(DIS_OPT_LABELS|DIS_OPT_SUBS|DIS_OPT_CONSTS|DIS_OPT_STR|DIS_OPT_SIMPLE_FUNCS);
1014 int dis_arch=FW_ARCH_ARMv7;
1015 int do_propset=0;
1016 if(argc < 2) {
1017 usage();
1018 }
1019 int i;
1020 for(i=1; i < argc; i++) {
1021 if ( strncmp(argv[i],"-c=",3) == 0 ) {
1022 dis_count=strtoul(argv[i]+3,NULL,0);
1023 }
1024 else if ( strncmp(argv[i],"-o=",3) == 0 ) {
1025 offset=strtoul(argv[i]+3,NULL,0);
1026 }
1027 else if ( strncmp(argv[i],"-s=",3) == 0 ) {
1028 if(!isdigit(argv[i][3])) {
1029 dis_start_fn = argv[i]+3;
1030 } else {
1031 dis_start=strtoul(argv[i]+3,NULL,0);
1032 }
1033 }
1034 else if ( strncmp(argv[i],"-e=",3) == 0 ) {
1035 dis_end=strtoul(argv[i]+3,NULL,0);
1036 }
1037 else if ( strncmp(argv[i],"-f=",3) == 0 ) {
1038 if ( strcmp(argv[i]+3,"chdk") == 0 ) {
1039 dis_opts |= DIS_OPT_FMT_CHDK;
1040 dis_opts |= DIS_OPT_ADR_LDR;
1041 } else if ( strcmp(argv[i]+3,"objdump") == 0 ) {
1042 dis_opts |= DIS_OPT_FMT_OBJDUMP;
1043 } else {
1044 fprintf(stderr,"unknown output format %s\n",argv[i]+3);
1045 usage();
1046 }
1047 }
1048 else if ( strcmp(argv[i],"-armv5") == 0 ) {
1049 dis_arch=FW_ARCH_ARMv5;
1050 }
1051 else if ( strcmp(argv[i],"-stubs") == 0 ) {
1052 strcpy(stubs_dir,".");
1053 }
1054 else if ( strncmp(argv[i],"-stubs=",7) == 0 ) {
1055 strcpy(stubs_dir,argv[i]+7);
1056 }
1057 else if ( strncmp(argv[i],"-props=",7) == 0 ) {
1058 do_propset=strtoul(argv[i]+7,NULL,0);
1059 }
1060 else if ( strcmp(argv[i],"-v") == 0 ) {
1061 verbose++;
1062 }
1063 else if ( strcmp(argv[i],"-d") == 0 ) {
1064 dis_opts |= DIS_OPT_DETAIL_ALL;
1065 }
1066 else if ( strcmp(argv[i],"-noloc") == 0 ) {
1067 dis_opts &= ~DIS_OPT_LABELS;
1068 }
1069 else if ( strcmp(argv[i],"-nosub") == 0 ) {
1070 dis_opts &= ~DIS_OPT_SUBS;
1071 }
1072 else if ( strcmp(argv[i],"-noconst") == 0 ) {
1073 dis_opts &= ~DIS_OPT_CONSTS;
1074 }
1075 else if ( strcmp(argv[i],"-nostr") == 0 ) {
1076 dis_opts &= ~DIS_OPT_STR;
1077 }
1078 else if ( strcmp(argv[i],"-noadrldr") == 0 ) {
1079 dis_opts &= ~DIS_OPT_ADR_LDR;
1080 }
1081 else if ( strcmp(argv[i],"-nofwdata") == 0 ) {
1082 do_fw_data_init = 0;
1083 }
1084 else if ( strcmp(argv[i],"-nosimplefunc") == 0 ) {
1085 dis_opts &= ~DIS_OPT_SIMPLE_FUNCS;
1086 }
1087 else if ( strcmp(argv[i],"-adrldr") == 0 ) {
1088 dis_opts |= DIS_OPT_ADR_LDR;
1089 }
1090 else if ( strcmp(argv[i],"-jfw") == 0 ) {
1091 dis_opts |= DIS_OPT_JMP_BACK;
1092 }
1093 else if ( strcmp(argv[i],"-eret") == 0 ) {
1094 dis_opts |= DIS_OPT_END_RET;
1095 }
1096 else if ( strncmp(argv[i],"-eret=",6) == 0 ) {
1097 dis_opts |= DIS_OPT_END_RET;
1098 dis_end_ret_count=strtoul(argv[i]+6,NULL,0);
1099 }
1100 else if ( strcmp(argv[i],"-d-const") == 0 ) {
1101 dis_opts |= DIS_OPT_DETAIL_CONST;
1102 }
1103 else if ( strcmp(argv[i],"-d-group") == 0 ) {
1104 dis_opts |= DIS_OPT_DETAIL_GROUP;
1105 }
1106 else if ( strcmp(argv[i],"-d-op") == 0 ) {
1107 dis_opts |= DIS_OPT_DETAIL_OP;
1108 }
1109 else if ( strcmp(argv[i],"-d-addr") == 0 ) {
1110 dis_opts |= DIS_OPT_DETAIL_ADDR;
1111 }
1112 else if ( strcmp(argv[i],"-d-bin") == 0 ) {
1113 dis_opts |= DIS_OPT_DETAIL_BIN;
1114 }
1115 else if ( argv[i][0]=='-' ) {
1116 fprintf(stderr,"unknown option %s\n",argv[i]);
1117 usage();
1118 } else {
1119 if(!dumpname) {
1120 dumpname=argv[i];
1121 } else if(load_addr == 0xFFFFFFFF) {
1122 load_addr=strtoul(argv[i],NULL,0);
1123 } else {
1124 fprintf(stderr,"unexpected %s\n",argv[i]);
1125 usage();
1126 }
1127 }
1128 }
1129 if(!dumpname) {
1130 fprintf(stderr,"missing input file\n");
1131 usage();
1132 }
1133 struct stat st;
1134 if(stat(dumpname,&st) != 0) {
1135 fprintf(stderr,"bad input file %s\n",dumpname);
1136 return 1;
1137 }
1138 int dumpsize = st.st_size;
1139
1140 if(load_addr==0xFFFFFFFF) {
1141 fprintf(stderr,"missing load address\n");
1142 usage();
1143 }
1144
1145 firmware fw;
1146 if(stubs_dir[0]) {
1147 dis_opts |= (DIS_OPT_STUBS|DIS_OPT_STUBS_LABEL);
1148 fw.sv = new_stub_values();
1149 char stubs_path[300];
1150 sprintf(stubs_path,"%s/%s",stubs_dir,"funcs_by_name.csv");
1151 load_funcs(fw.sv, stubs_path);
1152 sprintf(stubs_path,"%s/%s",stubs_dir,"stubs_entry.S");
1153 load_stubs(fw.sv, stubs_path, 1);
1154 sprintf(stubs_path,"%s/%s",stubs_dir,"stubs_min.S");
1155 load_stubs(fw.sv, stubs_path, 1);
1156 sprintf(stubs_path,"%s/%s",stubs_dir,"stubs_entry_2.S");
1157 load_stubs(fw.sv, stubs_path, 1);
1158 }
1159 if(do_propset) {
1160 fw.sv->propcases = NULL;
1161 if(!(dis_opts & DIS_OPT_STUBS)) {
1162 fprintf(stderr,"-props requires -stubs\n");
1163 usage();
1164 }
1165 char props_path[300];
1166 sprintf(props_path,"%s/../../../../include/propset%d.h",stubs_dir,do_propset);
1167
1168 load_propcases(fw.sv, props_path);
1169 if(!fw.sv->propcases) {
1170 fprintf(stderr,"propset %d load failed\n",do_propset);
1171 exit(1);
1172 }
1173 osig *ostub=find_sig(fw.sv->stubs,"SetPropertyCase");
1174 if(!ostub || !ostub->val) {
1175 fprintf(stderr,"SetPropertyCase not found, ignoring -props\n");
1176 set_prop = 0;
1177 } else {
1178 set_prop = ostub->val;
1179 }
1180 ostub=find_sig(fw.sv->stubs,"GetPropertyCase");
1181 if(!ostub || !ostub->val) {
1182 fprintf(stderr,"GetPropertyCase not found, ignoring -props\n");
1183 get_prop = 0;
1184 } else {
1185 get_prop = ostub->val;
1186 }
1187 if(get_prop && set_prop) {
1188 dis_opts |= DIS_OPT_PROPS;
1189 } else {
1190 do_propset=0;
1191 }
1192 }
1193 if(dis_start_fn) {
1194 if(!stubs_dir[0]) {
1195 fprintf(stderr,"-s with function name requires -stubs (did you forget an 0x?)\n");
1196 usage();
1197 }
1198 osig *ostub=find_sig(fw.sv->stubs,dis_start_fn);
1199 if(!ostub || !ostub->val) {
1200 fprintf(stderr,"start function %s not found (did you forget an 0x?)\n",dis_start_fn);
1201 return 1;
1202 }
1203 dis_start=ostub->val;
1204 }
1205
1206 if(offset != 0xFFFFFFFF) {
1207 if(dis_start) {
1208 fprintf(stderr,"both start and and offset given\n");
1209 usage();
1210 }
1211 dis_start = offset+load_addr;
1212 }
1213 if(dis_end) {
1214 if(dis_count) {
1215 fprintf(stderr,"both end and count given\n");
1216 usage();
1217 }
1218 if(dis_end > load_addr+dumpsize) {
1219 fprintf(stderr,"end > load address + size\n");
1220 usage();
1221 }
1222 if(dis_end < dis_start) {
1223 fprintf(stderr,"end < start\n");
1224 usage();
1225 }
1226 dis_count=(dis_end-dis_start)/2;
1227 }
1228
1229 if((dis_count==0) && (dis_opts & DIS_OPT_END_RET)) {
1230 dis_count=500;
1231 }
1232 if(dis_count==0) {
1233 fprintf(stderr,"missing instruction count\n");
1234 usage();
1235 }
1236
1237 if(dis_end==0) {
1238 dis_end=dis_start + dis_count * 4;
1239 }
1240 if((dis_opts & (DIS_OPT_FMT_CHDK | DIS_OPT_FMT_OBJDUMP)) == (DIS_OPT_FMT_CHDK | DIS_OPT_FMT_OBJDUMP)) {
1241 fprintf(stderr,"multiple -f options not allowed\n");
1242 usage();
1243 }
1244 if(dis_opts & (DIS_OPT_FMT_CHDK)) {
1245 comment_start = "//";
1246 }
1247
1248 firmware_load(&fw,dumpname,load_addr,dis_arch);
1249 firmware_init_capstone(&fw);
1250 if(do_fw_data_init) {
1251 firmware_init_data_ranges(&fw);
1252 }
1253
1254
1255 if(dis_start < fw.base) {
1256 adr_range_t *ar=adr_get_range(&fw,dis_start);
1257 if(!ar || ar->type != ADR_RANGE_RAM_CODE) {
1258 fprintf(stderr,"invalid start address 0x%08x\n",dis_start);
1259 return 1;
1260 }
1261 }
1262
1263 if(verbose) {
1264 printf("%s %s size:0x%x start:0x%x instructions:%d opts:0x%x\n",comment_start,dumpname,dumpsize,dis_start,dis_count,dis_opts);
1265 }
1266
1267
1268 do_dis_range(&fw, dis_start, dis_count, dis_end, dis_end_ret_count, dis_opts);
1269
1270 firmware_unload(&fw);
1271
1272
1273 return 0;
1274 }