-
Notifications
You must be signed in to change notification settings - Fork 141
/
Copy pathtypes_builder.py
1410 lines (1225 loc) · 59.6 KB
/
types_builder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import idc, idaapi, idautils
idaapi.require("moduledata")
idaapi.require("common")
from common import read_mem, ADDR_SZ
import sys
sys.setrecursionlimit(10000)
STANDARD_PACKAGES = ['archive/tar', 'archive/zip', 'bufio', 'builtin', 'bytes', 'compress/bzip2', 'compress/flate', 'compress/gzip', 'compress/lzw', 'compress/zlib', 'container/heap', 'container/list', 'container/ring', 'context', 'crypto', 'crypto/aes', 'crypto/cipher', 'crypto/des', 'crypto/dsa', 'crypto/ecdsa', 'crypto/ed25519', 'crypto/elliptic', 'crypto/hmac', 'crypto/md5', 'crypto/rand', 'crypto/rc4', 'crypto/rsa', 'crypto/sha1', 'crypto/sha256', 'crypto/sha512', 'crypto/subtle', 'crypto/tls', 'crypto/x509', 'crypto/x509/pkix', 'database/sql', 'database/sql/driver', 'debug/dwarf', 'debug/elf', 'debug/gosym', 'debug/macho', 'debug/pe', 'debug/plan9obj', 'encoding', 'encoding/ascii85', 'encoding/asn1', 'encoding/base32', 'encoding/base64', 'encoding/binary', 'encoding/csv', 'encoding/gob', 'encoding/hex', 'encoding/json', 'encoding/pem', 'encoding/xml', 'errors', 'expvar', 'flag', 'fmt', 'go/ast', 'go/build', 'go/constant', 'go/doc', 'go/format', 'go/importer', 'go/parser', 'go/printer', 'go/scanner', 'go/token', 'go/types', 'hash', 'hash/adler32', 'hash/crc32', 'hash/crc64', 'hash/fnv', 'html', 'html/template', 'image', 'image/color', 'image/color/palette', 'image/draw', 'image/gif', 'image/jpeg', 'image/png', 'index/suffixarray', 'io', 'io/ioutil', 'log', 'log/syslog', 'math', 'math/big', 'math/bits', 'math/cmplx', 'math/rand', 'mime', 'mime/multipart', 'mime/quotedprintable', 'net', 'net/http', 'net/http/cgi', 'net/http/cookiejar', 'net/http/fcgi', 'net/http/httptest', 'net/http/httptrace', 'net/http/httputil', 'net/http/pprof', 'net/mail', 'net/rpc', 'net/rpc/jsonrpc', 'net/smtp', 'net/textproto', 'net/url', 'os', 'os/exec', 'os/signal', 'os/user', 'path', 'path/filepath', 'plugin', 'reflect', 'regexp', 'regexp/syntax', 'runtime', 'runtime/cgo', 'runtime/debug', 'runtime/pprof', 'runtime/race', 'runtime/trace', 'sort', 'strconv', 'strings', 'sync', 'sync/atomic', 'syscall', 'syscall/js', 'testing', 'testing/iotest', 'testing/quick', 'text/scanner', 'text/tabwriter', 'text/template', 'text/template/parse', 'time', 'unicode', 'unicode/utf16', 'unicode/utf8', 'unsafe']
#import IPython
class TypesParser():
'''
Parse and construct all the types
'''
RAW_TYPES = ['Bool','Int','Int8','Int16','Int32','Int64','Uint','Uint8','Uint16','Uint32','Uint64','Uintptr','Float32','Float64','Complex64','Complex128', 'UnsafePointer', 'String']
def __init__(self, firstmoduledata):
self.moddata = firstmoduledata
self.parsed_types = dict()
self.itabs = list()
def is_raw_type(self, kind):
return kind in self.RAW_TYPES
def build_all_types(self, depth=1):
common._info("Building all types...\n")
common._info(f"Typelinks address is {self.moddata.typelink_addr:#x}.")
for idx in range(self.moddata.type_cnt):
type_off = read_mem(self.moddata.typelink_addr + idx*4, forced_addr_sz=4) & 0xFFFFFFFF
if type_off == 0:
continue
type_addr = self.moddata.types_addr + type_off
idc.set_cmt(self.moddata.typelink_addr + idx*4, f"type @ {type_addr:#x}", 0)
idaapi.auto_wait()
common._debug(f"{idx+1}th type, offset: {type_off:#x}, addr: {type_addr:#x}")
if type_addr in self.parsed_types.keys():
common._debug(" "*depth + 'already parsed')
continue
#print self.parsed_types.keys()
#try:
self.parse_type(type_addr=type_addr)
#except Exception as e:
# common._error("Failed to parse type_off( 0x%x ) @ 0x%x" % (type_off, type_addr))
# raise Exception(e)
common._info(f"Standard types building finished. Total types count: {len(self.parsed_types.keys())}")
self._parse_extra_types()
def _parse_extra_types(self):
'''
Parse extra types which missed by standard moduledata types
'''
common._debug("Parsing extra types...")
runtime_newobject_addr = idc.get_name_ea_simple("runtime_newobject")
if runtime_newobject_addr == idc.BADADDR:
common._debug("runtime_newobject not found")
return
std_types_cnt = len(self.parsed_types.keys())
for xrf in idautils.XrefsTo(runtime_newobject_addr):
if xrf.type != 17: # type 17: Code_Near_Call
continue
xrf_addr = xrf.frm
target_type_addr = idc.BADADDR
if common.CPU_ARCH == "x86" or common.CPU_ARCH == "x64": # x86/x64 Little Endian
target_type_addr = _get_target_addr_x86(xrf_addr)
elif common.CPU_ARCH == "arm" and common.ADDR_SZ == 4: # ARM 32-bit Little Endian
target_type_addr = _get_target_addr_arm32(xrf_addr)
elif common.CPU_ARCH == "arm" and common.ADDR_SZ == 8: # ARM 64-bit Little Endian
target_type_addr = _get_target_addr_arm64(xrf_addr)
elif common.CPU_ARCH.startswith("mips") and common.ADDR_SZ == 4: # MIPS 32
target_type_addr = _get_target_addr_mips32(xrf_addr)
if target_type_addr == idc.BADADDR:
common._debug("Failed to find target type addr for @ {xrf_addr:#x}")
continue
if target_type_addr not in self.parsed_types.keys():
common._debug(f"Parsing extra type @ {target_type_addr:#x}, xrf_from:@ {xrf_addr:#x}")
self.parse_type(type_addr=target_type_addr)
extra_type_cnt = len(self.parsed_types.keys()) - std_types_cnt
common._info(f"Extra types building done, total count: {extra_type_cnt}")
def parse_type(self, type_addr=idc.BADADDR, depth=1):
if type_addr == 0 or type_addr == idc.BADADDR:
return None
if type_addr in self.parsed_types.keys():
common._debug(" "*depth + 'already parsed')
return self.parsed_types[type_addr].rtype
common._debug(f"Parsing type @ {type_addr:#x}")
rtype = RType(type_addr, self.moddata, self)
rtype.parse()
common._debug(f"Type name @ {type_addr:#x}: {rtype.name}")
if rtype.size == 0:
common._info(" "*depth + f"> WARNNING: empty type @ {type_addr:#x}")
# parse the specific kind of data type
if rtype.get_kind() == "Ptr":
ptr_type = PtrType(type_addr, self, rtype)
self.parsed_types[type_addr] = ptr_type
ptr_type.parse()
common._debug(" "*depth + ptr_type.name)
elif rtype.get_kind() == "Struct":
st_type = StructType(type_addr, self, rtype)
self.parsed_types[type_addr] = st_type
st_type.parse()
common._debug(" "*depth + st_type.name)
elif rtype.get_kind() == "Array":
arr_type = ArrayType(type_addr, self, rtype)
self.parsed_types[type_addr] = arr_type
arr_type.parse()
common._debug(" "*depth + arr_type.name)
elif rtype.get_kind() == "Slice":
slice_type = SliceType(type_addr, self, rtype)
self.parsed_types[type_addr] = slice_type
slice_type.parse()
common._debug(" "*depth + slice_type.name)
elif rtype.get_kind() == "Interface":
itype = InterfaceType(type_addr, self, rtype)
self.parsed_types[type_addr] = itype
itype.parse()
common._debug(" "*depth + itype.name)
elif rtype.get_kind() == "Chan":
ch_type = ChanType(type_addr, self, rtype)
self.parsed_types[type_addr] = ch_type
ch_type.parse()
common._debug(" "*depth + ch_type.name)
elif rtype.get_kind() == "Func":
func_type = FuncType(type_addr, self, rtype)
self.parsed_types[type_addr] = func_type
func_type.parse()
common._debug(" "*depth + func_type.name)
elif rtype.get_kind() == "Map":
map_type = MapType(type_addr, self, rtype)
self.parsed_types[type_addr] = map_type
map_type.parse()
common._debug(" "*depth + map_type.name)
elif self.is_raw_type(rtype.get_kind()):
self.parsed_types[type_addr] = RawType(type_addr, rtype)
common._debug(" "*depth + rtype.name)
else:
raise Exception(f"Unknown type (kind:{rtype.get_kind()})")
# process uncommon type, i.e. types with mothods
#if rtype.get_kind() != "Map" and rtype.is_uncomm():
if rtype.is_uncomm():
prim_type = self.parsed_types[type_addr]
uncomm_type = UncommonType(prim_type, self)
self.parsed_types[type_addr] = uncomm_type
uncomm_type.parse()
return rtype
def has_been_parsed(self, addr):
return (addr in self.parsed_types.keys())
class RType():
'''
A single RType struct
Refer: https://golang.org/src/reflect/type.go
type rtype struct {
size uintptr
ptrdata uintptr // number of bytes in the type that can contain pointers
hash uint32 // hash of type; avoids computation in hash tables
tflag tflag // extra type information flags
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
'''
# Refer: https://golang.org/pkg/reflect/#Kind
TYPE_KINDS = ['Invalid Kind','Bool','Int','Int8','Int16','Int32','Int64','Uint','Uint8','Uint16','Uint32','Uint64','Uintptr','Float32','Float64','Complex64','Complex128','Array','Chan','Func','Interface','Map','Ptr','Slice','String','Struct','UnsafePointer']
# see https://golang.org/src/reflect/type.go for constants definition
TFLAG_UNCOMM = 0x1
TFLAG_STARPREFIX = 0x2
TFLAG_NAMED = 0x4
KIND_DIRECT_IFACE = 1 << 5
KIND_GCPROG = 1 << 6 # Type.gc points to GC program
KIND_MASK = (1 << 5) - 1
def __init__(self, addr, firstmoduledata, type_parser):
self.addr = addr
self.moddata = firstmoduledata
self.type_parser = type_parser
self.size = 0
self.ptrdata = 0
self.hash = None
self.tflag = None
self.align = 0
self.field_align = 0
self.kind = 0
self.alg = None
self.gcdata = None
self.name_off = 0
self.name_addr = idc.BADADDR
self.name_obj = None
self.name = ""
self.ptrtothis = None
self.ptrtothis_off = 0
self.ptrtothis_addr = idc.BADADDR
self.self_size = 0x20 if ADDR_SZ == 4 else 0x30
def parse(self):
common._debug(f"RType @ {self.addr:#x}")
self.size = read_mem(self.addr)
self.ptrdata = read_mem(self.addr + ADDR_SZ)
self.hash = read_mem(self.addr + 2*ADDR_SZ, forced_addr_sz = 4)
self.tflag = idc.get_wide_byte(self.addr + 2*ADDR_SZ + 4) & 0xFF
self.align = idc.get_wide_byte(self.addr + 2*ADDR_SZ + 5) & 0xFF
self.field_align = idc.get_wide_byte(self.addr + 2*ADDR_SZ + 6) & 0xFF
self.kind = idc.get_wide_byte(self.addr + 2*ADDR_SZ + 7) & 0xFF & RType.KIND_MASK
self.alg = read_mem(self.addr + 2*ADDR_SZ + 8)
self.gcdata = read_mem(self.addr + 3*ADDR_SZ + 8)
self.name_off = read_mem(self.addr + 4*ADDR_SZ + 8, forced_addr_sz=4) & 0xFFFFFFFF
if ADDR_SZ == 4:
self.name_addr = (self.moddata.types_addr + self.name_off) & 0xFFFFFFFF
else:
self.name_addr = (self.moddata.types_addr + self.name_off) & 0xFFFFFFFFFFFFFFFF
self.ptrtothis_off = read_mem(self.addr + 4*ADDR_SZ + 12, forced_addr_sz=4) & 0xFFFFFFFF
if self.ptrtothis_off > 0:
if ADDR_SZ == 4:
self.ptrtothis_addr = (self.moddata.types_addr + self.ptrtothis_off) & 0xFFFFFFFF
else:
self.ptrtothis_addr = (self.moddata.types_addr + self.ptrtothis_off) & 0xFFFFFFFFFFFFFFFF
idc.set_cmt(self.addr, "type size", 0)
idc.set_cmt(self.addr + ADDR_SZ, "type ptrdata", 0)
idc.set_cmt(self.addr + 2*ADDR_SZ, "type hash", 0)
tflag_comm = "tflag:"
if self.has_star_prefix():
tflag_comm += " Star Prefix;"
if self.is_named():
tflag_comm += " Named;"
if self.is_uncomm():
tflag_comm += " Uncommon"
idc.set_cmt(self.addr + 2*ADDR_SZ + 4, tflag_comm, 0)
common._debug(tflag_comm)
idc.set_cmt(self.addr + 2*ADDR_SZ + 5, "align", 0)
idc.set_cmt(self.addr + 2*ADDR_SZ + 6, "field align", 0)
idc.set_cmt(self.addr + 2*ADDR_SZ + 7, f"kind: {self.get_kind()}", 0)
if self.moddata.magic_number == common.MAGIC_112:
idc.set_cmt(self.addr + 2*ADDR_SZ + 8, "alg", 0)
else:
idc.set_cmt(self.addr + 2*ADDR_SZ + 8, "equal func", 0)
idc.set_cmt(self.addr + 3*ADDR_SZ + 8, "gcdata", 0)
common._debug(f"kind: {self.get_kind()}")
if self.ptrtothis_off > 0:
idc.set_cmt(self.addr + 4*ADDR_SZ + 12, f"ptrtothis addr: {self.ptrtothis_addr:#x}", 0)
else:
idc.set_cmt(self.addr + 4*ADDR_SZ + 12, "ptrtothis addr", 0)
idaapi.auto_wait()
self.name_obj = Name(self.name_addr, self.moddata)
self.name_obj.parse(self.has_star_prefix())
self.name = self.name_obj.simple_name
idc.set_cmt(self.addr + 4*ADDR_SZ + 8, f"name(@ {self.name_addr:#x} ): {self.name_obj.orig_name_str}", 0)
common._debug(f"name(@ {self.name_addr:#x} ): {self.name_obj.orig_name_str}")
# if a raw type is un-named, and name string is erased, the name it as it's kind string
if len(self.name) == 0 and self.type_parser.is_raw_type(self.get_kind()) and not self.is_named():
self.name = self.get_kind()
# if an un-raw type is named, then concat a kind string as suffix with it's name
if len(self.name) > 0 and self.is_named() and not self.type_parser.is_raw_type(self.get_kind()):
self.name += ("_%s" % self.get_kind().lower())
if self.get_kind() == "Struct" and not self.is_named(): # un-named struct type
self.name = "_struct_"
if self.get_kind() == "Func" and not self.is_named(): # un-named func type
self.name = "_func_"
if self.get_kind() == "Ptr":
self.name += "_ptr"
if len(self.name) > 0:
idc.set_name(self.addr, self.name, flags=idaapi.SN_FORCE)
idaapi.auto_wait()
# parse type pointer
if self.ptrtothis_off > 0 and self.ptrtothis_addr != idc.BADADDR:
if self.type_parser.has_been_parsed(self.ptrtothis_addr):
self.ptrtothis = self.type_parser.parsed_types[self.ptrtothis_addr]
else:
self.ptrtothis = self.type_parser.parse_type(type_addr=self.ptrtothis_addr)
idaapi.auto_wait()
common._debug(f"RType @ {self.addr:#x} parse finished.")
def get_kind(self):
return self.TYPE_KINDS[self.kind]
def has_star_prefix(self):
return self.tflag & RType.TFLAG_STARPREFIX != 0
def is_named(self):
return self.tflag & RType.TFLAG_NAMED != 0
def is_uncomm(self):
return self.tflag & RType.TFLAG_UNCOMM != 0
def get_name(self):
return self.name.simple_name
def __str__(self):
return self.get_name()
class Name():
'''
A rtype name struct
Refer: https://golang.org/src/reflect/type.go
name is an encoded type name with optional extra data.
The first byte is a bit field containing:
1<<0 the name is exported
1<<1 tag data follows the name
1<<2 pkgPath nameOff follows the name and tag
# If Go version < 1.8
The next two bytes are the data length:
l := uint16(data[1])<<8 | uint16(data[2])
Bytes [3:3+l] are the string data.
If tag data follows then bytes 3+l and 3+l+1 are the tag length,
with the data following.
# IF Go version >= 1.8
Following that, there is a varint-encoded length of the name,
followed by the name itself.
If tag data is present, it also has a varint-encoded length
followed by the tag itself.
If the import path follows, then 4 bytes at the end of
the data form a nameOff. The import path is only set for concrete
methods that are defined in a different package than their type.
If a name starts with "*", then the exported bit represents
whether the pointed to type is exported.
type name struct {
bytes *byte
}
# If Go version >= 1.18
// readVarint parses a varint as encoded by encoding/binary.
// It returns the number of encoded bytes and the encoded value.
func (n name) readVarint(off int) (int, int) {
v := 0
for i := 0; ; i++ {
x := *n.data(off+i, "read varint")
v += int(x&0x7f) << (7 * i)
if x&0x80 == 0 {
return i + 1, v
}
}
}
'''
EXPORTED = 0x1
FOLLOWED_BY_TAG = 0x2
FOLLOWED_BY_PKGPATH = 0x4
def __init__(self, addr, moddata):
self.addr = addr
self.moddata = moddata
self.len = 0
self.namestr_off = 0
self.is_exported = None
self.is_followed_by_tag = None
self.is_followed_by_pkgpath = None
self.orig_name_str = ""
self.name_str = ""
self.simple_name = ""
self.full_name = ""
self.pkg = ""
self.pkg_len = 0
self.tag = ""
self.tag_len = 0
self.tag_off_sz = 0
def parse(self, has_star_prefix):
common._debug(f"Name Type @ {self.addr:#x}")
flag_byte = idc.get_wide_byte(self.addr) & 0xFF
self.is_exported = flag_byte & self.EXPORTED != 0
self.is_followed_by_tag = flag_byte & self.FOLLOWED_BY_TAG != 0
self.is_followed_by_pkgpath = flag_byte & self.FOLLOWED_BY_PKGPATH != 0
self.orig_name_str = 'None'
if self.moddata.magic_number == common.MAGIC_112 or self.moddata.magic_number == common.MAGIC_116:
self.len = ((idc.get_wide_byte(self.addr + 1) & 0xFF << 8) | \
(idc.get_wide_byte(self.addr + 2) & 0xFF)) & 0xFFFF
if self.len > 0:
tmp_str = idc.get_bytes(self.addr + 3, self.len)
if tmp_str:
self.orig_name_str = tmp_str.decode("UTF-8", errors="ignore")
else:
self.namestr_off, self.len = self.read_varint(1)
if self.len > 0:
tmp_str = idc.get_bytes(self.addr + 1 + self.namestr_off, self.len)
if tmp_str:
self.orig_name_str = tmp_str.decode("UTF-8", errors="ignore")
self.name_str = self.orig_name_str
# delete star_prefix:
while True:
if len(self.name_str) > 0 and self.name_str[0] == '*':
self.name_str = self.name_str[1:]
else:
break
if self.is_followed_by_tag:
if self.moddata.magic_number == common.MAGIC_112 or self.moddata.magic_number == common.MAGIC_116:
self.tag_len = (idc.get_wide_byte(self.addr+ 3 + self.len) & 0xFF << 8) | \
(idc.get_wide_byte(self.addr + 3 + self.len + 1) & 0xFF)
tmp_tag = idc.get_bytes(self.addr + 3 + self.len + 2, self.tag_len)
if tmp_tag:
self.tag = tmp_tag.decode("UTF-8", errors="ignore")
else:
if self.len > 0 and self.namestr_off > 0:
self.tag_off_sz, self.tag_len = self.read_varint(1 + self.namestr_off + self.len)
if self.tag_len > 0:
tmp_tag = idc.get_bytes(self.addr + self.tag_off_sz, self.tag_len)
if tmp_tag:
self.tag = tmp_tag.decode("UTF-8", errors="ignore")
# if name was reased, the replace name string with tag string
if (not self.name_str or len(self.name_str) == 0) and self.tag and self.tag_len > 0:
self.name_str = self.tag
self.len = self.tag_len
if self.is_followed_by_pkgpath:
if self.moddata.magic_number == common.MAGIC_112 or self.moddata.magic_number == common.MAGIC_116:
pkgpath_off_addr = self.addr + 3 + self.len
if self.is_followed_by_tag:
pkgpath_off_addr += (self.tag_len + 2)
else:
pkgpath_off_addr = self.addr + 1 + self.namestr_off + self.len
if self.is_followed_by_tag:
pkgpath_off_addr += (self.tag_len + self.tag_off_sz)
pkgpath_off = read_mem(pkgpath_off_addr, forced_addr_sz=4)
common._debug(f"pkgpath_off: {pkgpath_off:#x}")
if pkgpath_off > 0 and pkgpath_off != 0xffffffff: # if error return 0xffffffff
# if c_int(pkgpath_off).value > 0:
pkgpath_addr = self.moddata.types_addr + pkgpath_off
pkgpath_name_obj = Name(pkgpath_addr, self.moddata)
common._debug(f"pkgpath_addr: {pkgpath_addr:#x}")
# idc.auto_wait()
pkgpath_name_obj.parse(False)
self.pkg = pkgpath_name_obj.name_str
self.pkg_len = len(self.pkg)
if self.pkg_len:
idc.set_cmt(pkgpath_off_addr, f"pkgpath(@ {pkgpath_addr:#x}): {self.pkg}", 0)
idaapi.auto_wait()
self.full_name = "%s%s%s" % (self.pkg if self.pkg else "", ("_%s" % self.name_str) \
if self.pkg else self.name_str, ('_%s' % self.tag) if self.tag else "")
self.simple_name = "%s%s" % (self.pkg if self.pkg else "", ("_%s" % self.name_str) \
if self.pkg else self.name_str)
flag_comm_str = "flag: "
if self.is_exported:
flag_comm_str += "exported"
if self.is_followed_by_tag:
if self.is_exported:
flag_comm_str += ", followed by tag"
else:
flag_comm_str += "followed by tag"
if self.is_followed_by_pkgpath:
if self.is_exported or self.is_followed_by_tag:
flag_comm_str += ", followed by pkgpath"
else:
flag_comm_str += "followed by pkgpath"
if len(flag_comm_str) > 6: # has valid flag
idc.set_cmt(self.addr, flag_comm_str, 0)
idaapi.auto_wait()
if self.moddata.magic_number == common.MAGIC_112 or self.moddata == common.MAGIC_116:
idc.create_strlit(self.addr + 3, self.addr + 3 + self.len)
else:
idc.create_strlit(self.addr + 1 + self.namestr_off, self.addr + 1 + self.namestr_off + self.len)
idaapi.auto_wait()
if self.is_followed_by_tag:
if self.moddata.magic_number == common.MAGIC_112 or self.moddata == common.MAGIC_116:
idc.create_strlit(self.addr + 1 + self.namestr_off + self.tag_off_sz, \
self.addr + 1 + self.namestr_off + self.tag_off_sz + self.tag_len)
idc.set_cmt(self.addr + 1 + self.namestr_off + self.tag_off_sz, f"tag of @ {self.addr:#x}", 0)
else:
idc.create_strlit(self.addr + 3 + self.len + 2, self.addr + 3 + self.len + 2 + self.tag_len)
idaapi.auto_wait()
def read_varint(self, offset):
'''
For Go 1.18+, name bytes are encoded by Varint
Refer:
- https://go.dev/src/reflect/type.go
- https://developers.google.com/protocol-buffers/docs/encoding
'''
val = 0
idx = 0
while True:
x = idc.get_wide_byte(self.addr + offset + idx) & 0xFF
val += int(x & 0x7F) << (7 * idx)
if (x & 0x80) & 0xFF == 0:
return idx + 1, val
idx += 1
class PtrType():
'''
Pointer type
Refer: https://golang.org/src/reflect/type.go
type ptrType struct {
rtype
elem *rtype // pointer element (pointed at) type
}
'''
def __init__(self, addr, type_parser, rtype):
self.addr = addr
self.type_parser = type_parser
self.rtype = rtype
self.size = rtype.self_size + ADDR_SZ
self.target_type_addr = idc.BADADDR
self.target_rtype = None
self.target_rtype_origname = ""
self.name = ""
def parse(self):
common._debug(f"PtrType @ {self.addr:#x}")
self.target_type_addr = read_mem(self.addr + self.rtype.self_size)
if self.type_parser.has_been_parsed(self.target_type_addr):
self.target_rtype = self.type_parser.parsed_types[self.target_type_addr]
self.target_rtype_origname = self.target_rtype.rtype.name_obj.orig_name_str
else:
self.target_rtype = self.type_parser.parse_type(type_addr=self.target_type_addr)
self.target_rtype_origname = self.target_rtype.name_obj.orig_name_str
if self.target_rtype:
self.name = self.target_rtype.name + "_ptr"
idc.set_cmt(self.addr + self.rtype.self_size, f"target rtype: {self.target_rtype_origname}", 0)
idaapi.auto_wait()
common._debug(f"target rtype: {self.target_rtype_origname}")
common._debug(f"PtrType @ {self.addr:#x} parse finished.")
def __str__(self):
return self.name
class StructType():
'''
Struct type
Refer: https://golang.org/src/reflect/type.go
type structType struct {
rtype
pkgPath name // !! pointer
fields []structField // sorted by offset
}
'''
def __init__(self, addr, type_parser, rtype):
self.addr = addr
self.type_parser = type_parser
self.rtype = rtype
self.size = self.rtype.self_size + 4 * ADDR_SZ
self.fields = list()
self.pkg_path_addr = idc.BADADDR
self.pkg_path_obj = None
self.pkg_path = ""
self.name = rtype.name
def parse(self):
common._debug(f"Struct Type @ {self.addr:#x}")
# parse pkg path
self.pkg_path_addr = read_mem(self.addr + self.rtype.self_size)
if self.pkg_path_addr > 0 and self.pkg_path_addr != idc.BADADDR:
common._debug(f"self.pkg_path_addr {self.pkg_path_addr:#x}.")
self.pkg_path_obj = Name(self.pkg_path_addr, self.type_parser.moddata)
self.pkg_path_obj.parse(False)
self.pkg_path = self.pkg_path_obj.simple_name
# parse fields
fields_start_addr = read_mem(self.addr + self.rtype.self_size + ADDR_SZ)
fields_cnt = read_mem(self.addr + self.rtype.self_size + 2*ADDR_SZ)
fields_cap = read_mem(self.addr + self.rtype.self_size + 3*ADDR_SZ)
for idx in range(fields_cnt):
field = StructFiled(fields_start_addr + idx*3*ADDR_SZ, self.type_parser)
field.parse()
self.fields.append(field)
idc.set_cmt(self.addr + self.rtype.self_size, "pkg path%s" % \
(f"(@ {self.pkg_path_addr:#x}): {self.pkg_path}" if (self.pkg_path_addr>0 and len(self.pkg_path)>0) else ""), 0)
idc.set_cmt(self.addr + self.rtype.self_size + 2*ADDR_SZ, f"fields count: {fields_cnt:#x}", 0)
idc.set_cmt(self.addr + self.rtype.self_size + 3*ADDR_SZ, f"fileds capacity: {fields_cap:#x}", 0)
idaapi.auto_wait()
common._debug("Struct pkg path: %s" % (f"(@ {self.pkg_path_addr:#x}): {self.pkg_path}" \
if (self.pkg_path_addr>0 and len(self.pkg_path)>0) else ""))
common._debug(f"Struct fields num: {fields_cnt:#x}")
if len(self.rtype.name) > 0 and fields_cnt > 0:
idc.set_cmt(self.addr + self.rtype.self_size + ADDR_SZ, "fields start address", 0)
idc.set_name(fields_start_addr, f"{self.rtype.name}_fields", flags=idaapi.SN_FORCE)
idaapi.auto_wait()
common._debug(f"Struct fields start addr: {fields_start_addr:#x}")
common._debug(f"Struct Type @ {self.addr:#x} parse finished.")
def __str__(self):
if self.rtype:
ret_str = f"> Struct: {self.rtype.name} ( {len(self.fields)} fields)\n"
for f in self.fields:
ret_str += f"\t\t- {f}\n"
return ret_str
else:
return ""
class StructFiled():
'''
Struct filed
Refer: https://golang.org/src/reflect/type.go
type structField struct {
name name // name is always non-empty
typ *rtype // type of field
offsetEmbed uintptr // byte offset of field<<1 | isEmbedded
}
'''
def __init__(self, addr, type_parser):
self.addr = addr
self.type_parser = type_parser
self.name_obj_addr = idc.BADADDR
self.name_obj = None
self.name = ""
self.rtype_addr = idc.BADADDR
self.rtype = None
self.offset = 0
self.is_embeded = False
self.size = 3 * ADDR_SZ
def parse(self):
common._debug(f"Struct StructFiled @ {self.addr:#x}")
self.name_obj_addr = read_mem(self.addr)
if self.name_obj_addr == 0 or self.name_obj_addr == idc.BADADDR:
raise Exception(f"Invalid name address when parsing struct field @ {self.addr:#x}")
self.name_obj = Name(self.name_obj_addr, self.type_parser.moddata)
self.name_obj.parse(False)
self.name = self.name_obj.simple_name
self.rtype_addr = read_mem(self.addr + ADDR_SZ)
if self.rtype_addr == 0 or self.rtype_addr == idc.BADADDR:
raise Exception(f"Invalid rtype address when parsing struct field @ {self.addr:#x}")
if self.type_parser.has_been_parsed(self.rtype_addr):
self.rtype = self.type_parser.parsed_types[self.rtype_addr]
else:
self.rtype = self.type_parser.parse_type(type_addr=self.rtype_addr)
off_embeded = read_mem(self.addr + 2*ADDR_SZ)
self.offset = off_embeded >> 1
self.is_embeded = (off_embeded & 1) != 0
idc.set_cmt(self.addr, f"field name: { self.name_obj.name_str}", 0)
idaapi.auto_wait()
idc.set_cmt(self.addr + ADDR_SZ, f"field rtype: {self.rtype.name}", 0)
idaapi.auto_wait()
common._debug(f"Struct field name: {self.name_obj.name_str}")
common._debug(f"Struct field rtype: {self.rtype.name}")
common._debug(f"Struct StructFiled @ {self.addr:#x} parse finished.")
def __str__(self):
return self.name
class ArrayType():
'''
Array type
Refer: https://golang.org/src/reflect/type.go
type arrayType struct {
rtype
elem *rtype // array element type
slice *rtype // slice type
len uintptr
}
'''
def __init__(self, addr, type_parser, rtype):
self.addr = addr
self.type_parser = type_parser
self.rtype = rtype
self.name = rtype.name
self.size = rtype.self_size + 3*ADDR_SZ
self.elem_type = None
self.slice_type = None
self.len = 0
def parse(self):
common._debug(f"Array Type @ {self.addr:#x}")
elem_type_addr = read_mem(self.addr + self.rtype.self_size)
if self.type_parser.has_been_parsed(elem_type_addr):
self.elem_type = self.type_parser.parsed_types[elem_type_addr]
else:
self.elem_type = self.type_parser.parse_type(type_addr=elem_type_addr)
slice_type_addr = read_mem(self.addr + self.rtype.self_size + ADDR_SZ)
if self.type_parser.has_been_parsed(slice_type_addr):
self.slice_type = self.type_parser.parsed_types[slice_type_addr]
else:
self.slice_type = self.type_parser.parse_type(type_addr=slice_type_addr)
self.len = read_mem(self.addr + self.rtype.self_size + 2 * ADDR_SZ)
idc.set_cmt(self.addr + self.rtype.self_size, f"elem type: {self.elem_type.name}", 0)
idc.set_cmt(self.addr + self.rtype.self_size + ADDR_SZ, f"slice type: {self.slice_type.name}", 0)
idc.set_cmt(self.addr + self.rtype.self_size + 2 * ADDR_SZ, f"array length: {self.len}", 0)
idc.set_name(self.addr, f"{self.elem_type.name}_array", flags=idaapi.SN_FORCE)
idaapi.auto_wait()
common._debug(f"Array elem type: {self.elem_type.name}")
common._debug(f"Array slice type: {self.slice_type.name}")
common._debug(f"Array Type @ {self.addr:#x} parse finished.")
def __str__(self):
return f"{self.elem_type.name} array(len: {self.len})"
class SliceType():
'''
Slice type
Refer: https://golang.org/src/reflect/type.go
type sliceType struct {
rtype
elem *rtype // slice element type
}
'''
def __init__(self, addr, type_parser, rtype):
self.addr = addr
self.type_parser = type_parser
self.rtype = rtype
self.name = rtype.name
self.size = self.rtype.self_size + ADDR_SZ
def parse(self):
common._debug(f"Slice Type @ {self.addr:#x}")
self.elem_type_addr = read_mem(self.addr + self.rtype.self_size)
if self.type_parser.has_been_parsed(self.elem_type_addr):
self.elem_rtype = self.type_parser.parsed_types[self.elem_type_addr]
else:
self.elem_rtype = self.type_parser.parse_type(type_addr=self.elem_type_addr)
idc.set_cmt(self.addr + self.rtype.self_size, f"elem rtype: {self.elem_rtype.name}", 0)
idc.set_name(self.addr, f"{self.elem_rtype.name}_slice", flags=idaapi.SN_FORCE)
idaapi.auto_wait()
common._debug(f"Slice elem rtype: {self.elem_rtype.name}")
common._debug(f"Slice Type @ {self.addr:#x} parse finished.")
def __str__(self):
if self.elem_rtype:
return f"Slice {self.elem_rtype.name}"
else:
return ""
class InterfaceType():
'''
Interface type
Refer: https://golang.org/src/reflect/type.go
type interfaceType struct {
rtype
pkgPath name // import path
methods []imethod // sorted by hash
}
'''
def __init__(self, addr, type_parser, rtype):
self.addr = addr
self.type_parser = type_parser
self.rtype = rtype
self.size = rtype.self_size + 4*ADDR_SZ
self.pkg_path_addr = idc.BADADDR
self.pkg_path_obj = None
self.pkg_path = ""
self.name = rtype.name
self.methods = list()
def parse(self):
common._debug(f"Interface @ {self.addr:#x}")
# parse pkg path
self.pkg_path_addr = read_mem(self.addr + self.rtype.self_size)
if self.pkg_path_addr > 0 and self.pkg_path_addr != idc.BADADDR:
self.pkg_path_obj = Name(self.pkg_path_addr, self.type_parser.moddata)
self.pkg_path_obj.parse(False)
self.pkg_path = self.pkg_path_obj.name_str
# parse fields
methods_start_addr = read_mem(self.addr + self.rtype.self_size + ADDR_SZ)
methods_cnt = read_mem(self.addr + self.rtype.self_size + 2*ADDR_SZ)
methods_cap = read_mem(self.addr + self.rtype.self_size + 3*ADDR_SZ)
for idx in range(methods_cnt):
imeth = IMethodType(methods_start_addr + idx*2*4, self.type_parser)
imeth.parse()
self.methods.append(imeth)
idc.set_cmt(self.addr + self.rtype.self_size, "pkg path%s" % \
(f"(@ {self.pkg_path_addr:#x}): self.pkg_path" if (self.pkg_path_addr>0 and len(self.pkg_path)>0) else ""), 0)
idc.set_cmt(self.addr + self.rtype.self_size + 2*ADDR_SZ, f"methods count: {methods_cnt:#x}", 0)
idc.set_cmt(self.addr + self.rtype.self_size + 3*ADDR_SZ, f"methods capacity: {methods_cap:#x}", 0)
idaapi.auto_wait()
common._debug("Interface pkg path%s" % \
(f"(@ {self.pkg_path_addr:#x}): {self.pkg_path}" if (self.pkg_path_addr>0 and len(self.pkg_path)>0) else ""))
common._debug(f"Interface methods count: {methods_cnt:#x}")
if len(self.rtype.name) > 0:
idc.set_name(methods_start_addr, f"{self.rtype.name}_methods", flags=idaapi.SN_FORCE)
idaapi.auto_wait()
common._debug(f"Interface @ {self.addr:#x} parse finished.")
def __str__(self):
if self.rtype:
ret_str = f"> Interface: {self.rtype.name} ( {len(self.methods)} methods)\n"
for m in self.methods:
ret_str += f"\t\t- {m}\n"
return ret_str
else:
return ""
class IMethodType():
'''
IMethod type
Refer: https://golang.org/src/reflect/type.go
type imethod struct {
name nameOff // name of method
typ typeOff // .(*FuncType) underneath
}
'''
def __init__(self, addr, type_parser):
self.addr = addr
self.type_parser = type_parser
self.types_addr = type_parser.moddata.types_addr
self.size = 8
self.name_obj = None
self.name = ""
self.type = None
def parse(self):
common._debug(f"Imethod Type @ {self.addr:#x}")
name_off = read_mem(self.addr, forced_addr_sz=4)
if ADDR_SZ == 4:
name_addr = (self.types_addr + name_off) & 0xFFFFFFFF
else:
name_addr = (self.types_addr + name_off) & 0xFFFFFFFFFFFFFFFF
self.name_obj = Name(name_addr, self.type_parser.moddata)
self.name_obj.parse(False)
self.name = self.name_obj.simple_name
type_off = read_mem(self.addr+4, forced_addr_sz=4)
if ADDR_SZ == 4:
type_addr = (self.types_addr + type_off) & 0xFFFFFFFF
else:
type_addr = (self.types_addr + type_off) & 0xFFFFFFFFFFFFFFFF
if type_off > 0 and type_addr != idc.BADADDR:
if self.type_parser.has_been_parsed(type_addr):
self.type = self.type_parser.parsed_types[type_addr].rtype
else:
self.type = self.type_parser.parse_type(type_addr=type_addr)
if name_off > 0 and name_off != idc.BADADDR:
idc.set_cmt(self.addr, f"imethod name(@ {name_addr:#x}): {self.name}", 0)
idaapi.auto_wait()
common._debug(f"Interface imethod name(@ {name_addr:#x}): {self.name}")
if type_off > 0 and type_addr != idc.BADADDR:
idc.set_cmt(self.addr + 4, f"imethod type(@ {type_addr:#x}): {self.type.name_obj.name_str}", 0)
idaapi.auto_wait()
common._debug(f"Interface imethod type(@ {type_addr:#x}): {self.type.name_obj.name_str}")
common._debug(f"Imethod Type @ {self.addr:#x} parsed finished.")
def __str__(self):
if self.name:
return self.name_obj.full_name
else:
return ""
class ChanType():
'''
Channel type
Refer: https://golang.org/src/reflect/type.go
type chanType struct {
rtype
elem *rtype // channel element type
dir uintptr // channel direction (ChanDir)
}
'''
RECV_DIR = 1
SEND_DIR = 2
BOTH_DIR = 3
def __init__(self, addr, type_parser, rtype):
self.addr = addr
self.type_parser = type_parser
self.rtype = rtype
self.size = self.rtype.self_size + 2 * ADDR_SZ
self.direction = ""
self.name = ""
def parse(self):
common._debug(f"Channel Type @ {self.addr:#x}")
elem_type_addr = read_mem(self.addr + self.rtype.self_size)
if self.type_parser.has_been_parsed(elem_type_addr):
self.elem_type = self.type_parser.parsed_types[elem_type_addr]
else:
self.elem_type = self.type_parser.parse_type(type_addr=elem_type_addr)
self.elem_type.parse()
dir_code = read_mem(self.addr + self.rtype.self_size + ADDR_SZ)
self.direction = self.get_direction(dir_code)
self.name = f"channel {self.rtype.name} (direction: {self.direction})"
idc.set_cmt(self.addr + self.rtype.self_size, f"elem type: {self.elem_type.name}", 0)
idc.set_cmt(self.addr + self.rtype.self_size + ADDR_SZ, f"chan direction: {self.direction}", 0)
idaapi.auto_wait()
common._debug(f"Channel Type @ {self.addr:#x} parse finished.")
def get_direction(self, dir_code):
if dir_code == self.RECV_DIR:
return 'recv'
elif dir_code == self.SEND_DIR:
return 'send'
else:
return 'send & recv'
def __str__(self):
return self.name
class FuncType():
'''
Function Type
Refer: https://golang.org/src/reflect/type.go
type funcType struct {
rtype
inCount uint16
outCount uint16 // top bit is set if last input parameter is ...
padding uint32 // ! only on some architectures (e.g. x64)
}
Note: "A *rtype for each in and out parameter is stored in an array that
directly follows the funcType (and possibly its uncommonType)."
'''
VARIADIC_FLAG = 0x8000
def __init__(self, addr, type_parser, rtype):
self.addr = addr
self.type_parser = type_parser
self.rtype = rtype
self.para_cnt = 0
self.ret_cnt = 0
self.padding = -1
self.is_variadic = False
self.para_types = list()
self.para_type_addrs = list()
self.ret_types = list()
self.ret_type_addrs = list()
self.name = rtype.name
self.size = rtype.self_size + 2*2 # without padding
def parse(self):
common._debug(f"Func Type @ {self.addr:#x}")
self.para_cnt = read_mem(self.addr + self.rtype.self_size, forced_addr_sz=2) & 0xFFFF
self.ret_cnt = read_mem(self.addr + self.rtype.self_size + 2, forced_addr_sz=2) & 0xFFFF
if self.ret_cnt & FuncType.VARIADIC_FLAG:
self.is_variadic = True
self.ret_cnt = self.ret_cnt & 0x7FFF
self.padding = read_mem(self.addr + self.rtype.self_size + 4, forced_addr_sz=4) & 0xFFFFFFFF
if self.padding == 0: # skip padding if present
self.size += 4
curr_addr = self.addr + self.size
if self.rtype.is_uncomm():
curr_addr += UncommonType.SIZE
for in_idx in range(self.para_cnt):
curr_para_type = None