From 8b3bd0ecf6641a7e3a6e393bb71b39602d061fc7 Mon Sep 17 00:00:00 2001 From: virusdefender Date: Fri, 15 Sep 2023 15:07:22 +0800 Subject: [PATCH 1/3] parse field tag --- objfile/objfile.go | 104 ++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 30 deletions(-) diff --git a/objfile/objfile.go b/objfile/objfile.go index 2bfd60a..982a786 100644 --- a/objfile/objfile.go +++ b/objfile/objfile.go @@ -10,6 +10,7 @@ package objfile import ( "bytes" "encoding/binary" + "errors" "fmt" "io" "os" @@ -839,7 +840,7 @@ func (e *Entry) readVarint(address uint64) (int, int, error) { } } -func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr uint64, is64bit bool, littleendian bool) (name string, err error) { +func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr uint64, is64bit bool, littleendian bool) (name string, tag string, err error) { // name str (for <= 1.16 encodes length like this, beyond it uses a varint encoding) // The first byte is a bit field containing: // @@ -872,23 +873,24 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui case "1.5": fallthrough case "1.6": + // TODO: parse field tag // pointer to GoString nameLen, err := e.ReadPointerSizeMem(namePtr+ptrSize, is64bit, littleendian) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } deref, err := e.ReadPointerSizeMem(namePtr, is64bit, littleendian) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } name_raw, err := e.raw.read_memory(deref, nameLen) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } - return string(name_raw), nil + return string(name_raw), "", nil case "1.7": // types flags exists >= 1.7 fallthrough case "1.8": // type flag tflagExtraStart exists >= 1.8 @@ -908,22 +910,41 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui case "1.15": fallthrough case "1.16": - name_len_raw, err := e.raw.read_memory(namePtr, 3) + flag, err := e.raw.read_memory(namePtr, 1) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", errors.New("Failed to read flag") } - name_len := uint16(uint16(name_len_raw[1])<<8 | uint16(name_len_raw[2])) + name_len_raw, err := e.raw.read_memory(namePtr+1, 2) + if err != nil { + return "", "", fmt.Errorf("Failed to read name") + } + + name_len := uint16(uint16(name_len_raw[0])<<8 | uint16(name_len_raw[1])) name_raw, err := e.raw.read_memory(namePtr+3, uint64(name_len)) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } name := string(name_raw) + + if flag[0] == 3 { + tag_len_raw, err := e.raw.read_memory(namePtr+3+uint64(name_len), 2) + if err != nil { + return "", "", errors.New("Failed to read tag") + } + + tag_len := uint16(uint16(tag_len_raw[0])<<8 | uint16(tag_len_raw[1])) + tag_raw, err := e.raw.read_memory(namePtr+3+uint64(name_len)+2, uint64(tag_len)) + if err != nil { + return "", "", errors.New("Failed to read tag") + } + tag = string(tag_raw) + } if typeFlags&tflagExtraStar != 0 { - return name[1:], nil + return name[1:], tag, nil } else { - return name, nil + return name, tag, nil } case "1.17": fallthrough @@ -932,24 +953,39 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui case "1.19": fallthrough case "1.20": + flag, err := e.raw.read_memory(namePtr, 1) + if err != nil { + return "", "", errors.New("Failed to read flag") + } varint_len, namelen, err := e.readVarint(namePtr + 1) if err != nil { - return "", fmt.Errorf("Failed to read name") + // TODO: replace all misuses of fmt.Errorf to errors.New + return "", "", fmt.Errorf("Failed to read name") } - name_raw, err := e.raw.read_memory(namePtr+1+uint64(varint_len), uint64(namelen)) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } - name := string(name_raw) + + if flag[0] == 3 { + varint_len, taglen, err := e.readVarint(namePtr + 1 + uint64(varint_len) + uint64(namelen)) + if err != nil { + return "", "", errors.New("Failed to read tag") + } + tag_raw, err := e.raw.read_memory(namePtr+1+uint64(varint_len)+uint64(namelen)+uint64(varint_len), uint64(taglen)) + if err != nil { + return "", "", errors.New("Failed to read tag") + } + tag = string(tag_raw) + } if typeFlags&tflagExtraStar != 0 { - return name[1:], nil + return name[1:], tag, nil } else { - return name, nil + return name, tag, nil } } - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } func decodePtrSizeBytes(data []byte, is64bit bool, littleendian bool) (result uint64) { @@ -1056,7 +1092,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } - name, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1074,7 +1110,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } - name, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1093,7 +1129,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } - name, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1110,7 +1146,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } - name, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1140,7 +1176,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } name_ptr := moduleData.Types + uint64(rtype.Str) - name, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1156,7 +1192,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } name_ptr := moduleData.Types + uint64(rtype.Str) - name, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1186,7 +1222,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } name_ptr := moduleData.Types + uint64(rtype.Str) - name, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1202,7 +1238,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } name_ptr := moduleData.Types + uint64(rtype.Str) - name, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1597,9 +1633,13 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty field, found := parsedTypesIn.Get(typeAddr) if found { typeNameAddr := decodePtrSizeBytes(data[0:ptrSize], is64bit, littleendian) - typeName, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) + typeName, tag, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) if err == nil { - structDef += fmt.Sprintf("\n %-10s %s", typeName, field.(Type).Str) + if tag != "" { + structDef += fmt.Sprintf("\n %-10s %s `%s`", typeName, field.(Type).Str, tag) + } else { + structDef += fmt.Sprintf("\n %-10s %s", typeName, field.(Type).Str) + } cstructDef += fmt.Sprintf(" %-10s %s;\n", field.(Type).CStr, replace_cpp_keywords(typeName)) } } @@ -1690,9 +1730,13 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty field, found := parsedTypesIn.Get(typeAddr) if found { typeNameAddr := decodePtrSizeBytes(data[0:ptrSize], is64bit, littleendian) - typeName, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) + typeName, tag, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) if err == nil { - structDef += fmt.Sprintf("\n %-10s %s", typeName, field.(Type).Str) + if tag != "" { + structDef += fmt.Sprintf("\n %-10s %s `%s`", typeName, field.(Type).Str, tag) + } else { + structDef += fmt.Sprintf("\n %-10s %s", typeName, field.(Type).Str) + } cstructDef += fmt.Sprintf(" %-10s %s;\n", field.(Type).CStr, replace_cpp_keywords(typeName)) } } From 139bc912942422ec7b2f95462658bc71861f8290 Mon Sep 17 00:00:00 2001 From: virusdefender Date: Wed, 20 Sep 2023 11:10:27 +0800 Subject: [PATCH 2/3] parse interface method name --- objfile/objfile.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/objfile/objfile.go b/objfile/objfile.go index 982a786..f429726 100644 --- a/objfile/objfile.go +++ b/objfile/objfile.go @@ -1479,6 +1479,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty parsedTypesIn, _ = e.ParseType_impl(runtimeVersion, moduleData, typeAddr, is64bit, littleendian, parsedTypesIn) methodfunc, found := parsedTypesIn.Get(typeAddr) if found { + // TODO: parse method name interfaceDef += "\nmethod" + strconv.Itoa(i) + " " + methodfunc.(Type).Str cinterfaceDef += methodfunc.(Type).CStr + "method" + strconv.Itoa(i) + ";\n" } @@ -1566,11 +1567,16 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty typeAddr := moduleData.Types + uint64(method.Typ) parsedTypesIn, _ = e.ParseType_impl(runtimeVersion, moduleData, typeAddr, is64bit, littleendian, parsedTypesIn) + name_ptr := moduleData.Types + uint64(method.Name) + name, _, err := e.readRTypeName(runtimeVersion, 0, name_ptr, is64bit, littleendian) + if err != nil { + name = "method" + strconv.Itoa(i) + } methodfunc, found := parsedTypesIn.Get(typeAddr) if found { - interfaceDef += "\nmethod" + strconv.Itoa(i) + " " + methodfunc.(Type).Str - cinterfaceDef += methodfunc.(Type).CStr + " method" + strconv.Itoa(i) + ";\n" + interfaceDef += name + " " + methodfunc.(Type).Str + "\n" + cinterfaceDef += methodfunc.(Type).CStr + " " + name + ";\n" } } interfaceDef += "\n}" From 36920d96fb518fce471424f8f288f2fc93667fbf Mon Sep 17 00:00:00 2001 From: virusdefender Date: Wed, 20 Sep 2023 11:33:24 +0800 Subject: [PATCH 3/3] add some hack to reconstruct better golang code --- objfile/objfile.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/objfile/objfile.go b/objfile/objfile.go index f429726..8c7fc84 100644 --- a/objfile/objfile.go +++ b/objfile/objfile.go @@ -12,6 +12,7 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/mandiant/GoReSym/objabi" "io" "os" "sort" @@ -1575,7 +1576,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty methodfunc, found := parsedTypesIn.Get(typeAddr) if found { - interfaceDef += name + " " + methodfunc.(Type).Str + "\n" + interfaceDef += name + strings.TrimPrefix(methodfunc.(Type).Str, "func") + "\n" cinterfaceDef += methodfunc.(Type).CStr + " " + name + ";\n" } } @@ -1641,10 +1642,16 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty typeNameAddr := decodePtrSizeBytes(data[0:ptrSize], is64bit, littleendian) typeName, tag, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) if err == nil { + var fieldStr string + if field.(Type).kindEnum == objabi.KindStruct { + fieldStr = strings.TrimPrefix(field.(Type).Reconstructed, "type ") + } else { + fieldStr = field.(Type).Str + } if tag != "" { - structDef += fmt.Sprintf("\n %-10s %s `%s`", typeName, field.(Type).Str, tag) + structDef += fmt.Sprintf("\n %-10s %s `%s`", typeName, fieldStr, tag) } else { - structDef += fmt.Sprintf("\n %-10s %s", typeName, field.(Type).Str) + structDef += fmt.Sprintf("\n %-10s %s", typeName, fieldStr) } cstructDef += fmt.Sprintf(" %-10s %s;\n", field.(Type).CStr, replace_cpp_keywords(typeName)) } @@ -1738,10 +1745,16 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty typeNameAddr := decodePtrSizeBytes(data[0:ptrSize], is64bit, littleendian) typeName, tag, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) if err == nil { + var fieldStr string + if field.(Type).kindEnum == objabi.KindStruct { + fieldStr = strings.TrimPrefix(field.(Type).Reconstructed, "type ") + } else { + fieldStr = field.(Type).Str + } if tag != "" { - structDef += fmt.Sprintf("\n %-10s %s `%s`", typeName, field.(Type).Str, tag) + structDef += fmt.Sprintf("\n %-10s %s `%s`", typeName, fieldStr, tag) } else { - structDef += fmt.Sprintf("\n %-10s %s", typeName, field.(Type).Str) + structDef += fmt.Sprintf("\n %-10s %s", typeName, fieldStr) } cstructDef += fmt.Sprintf(" %-10s %s;\n", field.(Type).CStr, replace_cpp_keywords(typeName)) }