Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,37 @@ func TestAllVersions(t *testing.T) {
}
}

// --- Struct field Tag recovery tests ---
if v != "15" && v != "16" {
found_tagged := false
for _, typ := range data.Types {
if typ.Str == "main.TaggedStruct" && typ.Kind == "Struct" {
found_tagged = true
if !strings.Contains(typ.Reconstructed, `json:"id"`) {
t.Errorf("Go %s exported field tag json:id missing in TaggedStruct", v)
}
if !strings.Contains(typ.Reconstructed, `db:"user_id"`) {
t.Errorf("Go %s multi-tag db:user_id missing in TaggedStruct", v)
}
if !strings.Contains(typ.Reconstructed, `json:"name"`) {
t.Errorf("Go %s exported field tag json:name missing in TaggedStruct", v)
}
if !strings.Contains(typ.Reconstructed, `json:"password"`) {
t.Errorf("Go %s unexported field tag json:password missing in TaggedStruct", v)
}
if strings.Contains(typ.Reconstructed, "Active`") || strings.Contains(typ.Reconstructed, "`Active") {
t.Errorf("Go %s Active field has spurious backtick in TaggedStruct", v)
}
if strings.Contains(typ.Reconstructed, "``") {
t.Errorf("Go %s empty backtick pair in TaggedStruct Reconstructed", v)
}
}
}
if !found_tagged {
t.Errorf("Go %s TaggedStruct not found in types", v)
}
}

if len(data.StdFunctions) == 0 {
t.Errorf("Go %s std functions failed on %s: %s", v, file, err)
}
Expand Down
80 changes: 55 additions & 25 deletions objfile/objfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,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:
//
Expand Down Expand Up @@ -457,23 +457,23 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui
case "1.5":
fallthrough
case "1.6":
// pointer to GoString
// pointer to GoString — tag is a separate pointer in structField, not inline here
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
Expand All @@ -495,21 +495,32 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui
case "1.16":
name_len_raw, err := e.raw.read_memory(namePtr, 3)
if err != nil {
return "", fmt.Errorf("Failed to read name")
return "", "", fmt.Errorf("Failed to read name")
}

flag := name_len_raw[0]
name_len := uint16(uint16(name_len_raw[1])<<8 | uint16(name_len_raw[2]))
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 typeFlags&tflagExtraStar != 0 {
return name[1:], nil
} else {
return name, nil
name = name[1:]
}

if flag&(1<<1) != 0 {
tag_len_raw, err := e.raw.read_memory(namePtr+3+uint64(name_len), 2)
if err == nil {
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 name, string(tag_raw), nil
}
}
}
return name, "", nil
case "1.17":
fallthrough
case "1.18":
Expand All @@ -529,24 +540,39 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui
case "1.25":
fallthrough
case "1.26":
varint_len, namelen, err := e.readVarint(namePtr + 1)
flag_raw, err := e.raw.read_memory(namePtr, 1)
if err != nil {
return "", fmt.Errorf("Failed to read name")
return "", "", fmt.Errorf("Failed to read name")
}
flag := flag_raw[0]

name_raw, err := e.raw.read_memory(namePtr+1+uint64(varint_len), uint64(namelen))
nameVarintLen, namelen, err := e.readVarint(namePtr + 1)
if err != nil {
return "", fmt.Errorf("Failed to read name")
return "", "", fmt.Errorf("Failed to read name")
}

name_raw, err := e.raw.read_memory(namePtr+1+uint64(nameVarintLen), uint64(namelen))
if err != nil {
return "", "", fmt.Errorf("Failed to read name")
}

name := string(name_raw)
if typeFlags&tflagExtraStar != 0 {
return name[1:], nil
} else {
return name, nil
name = name[1:]
}

if flag&(1<<1) != 0 {
tagVarintLen, taglen, err := e.readVarint(namePtr + 1 + uint64(nameVarintLen) + uint64(namelen))
if err == nil && taglen > 0 {
tag_raw, err := e.raw.read_memory(namePtr+1+uint64(nameVarintLen)+uint64(namelen)+uint64(tagVarintLen), uint64(taglen))
if err == nil {
return name, string(tag_raw), nil
}
}
}
return name, "", 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) {
Expand Down Expand Up @@ -677,7 +703,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty
namePtr = moduleData.Types + rtype.Str
}

name, err = e.readRTypeName(runtimeVersion, rtype.Tflag, namePtr, is64bit, littleendian)
name, _, err = e.readRTypeName(runtimeVersion, rtype.Tflag, namePtr, is64bit, littleendian)
if err != nil {
return parsedTypesIn, fmt.Errorf("Failed to read type name")
}
Expand Down Expand Up @@ -1078,7 +1104,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty
parsedTypesIn, _ = e.ParseType_impl(runtimeVersion, moduleData, typeAddr, is64bit, littleendian, parsedTypesIn)

name_ptr := decodePtrSizeBytes(imethoddata[0:ptrSize], is64bit, littleendian)
name, err := e.readRTypeName(runtimeVersion, 0, name_ptr, is64bit, littleendian)
name, _, err := e.readRTypeName(runtimeVersion, 0, name_ptr, is64bit, littleendian)
if err != nil {
continue
}
Expand Down Expand Up @@ -1182,7 +1208,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty
parsedTypesIn, _ = e.ParseType_impl(runtimeVersion, moduleData, typeAddr, is64bit, littleendian, parsedTypesIn)

name_ptr := moduleData.Types + uint64(methodNameOff)
name, err := e.readRTypeName(runtimeVersion, 0, name_ptr, is64bit, littleendian)
name, _, err := e.readRTypeName(runtimeVersion, 0, name_ptr, is64bit, littleendian)
if err != nil {
continue
}
Expand Down Expand Up @@ -1253,7 +1279,7 @@ 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, _, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian)
if err == nil {
structDef += fmt.Sprintf("\n %-10s %s", typeName, field.(Type).Str)
cstructDef += fmt.Sprintf(" %-10s %s;\n", field.(Type).CStr, replace_cpp_keywords(typeName))
Expand Down Expand Up @@ -1358,9 +1384,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))
}
}
Expand Down
22 changes: 15 additions & 7 deletions testproject/main.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
/*Copyright (C) 2022 Mandiant, Inc. All Rights Reserved.*/
package main

import "fmt"

type structurea struct {
test string
import (
"fmt"
)

type TaggedStruct struct {
ID uint64 `json:"id" db:"user_id"`
Name string `json:"name"`
password string `json:"password"`
Active bool
}

func sum(s []int, c chan int) {
Expand All @@ -16,10 +21,13 @@ func sum(s []int, c chan int) {
}

func main() {
var structa structurea
structa.test = "hi"
var Ts TaggedStruct
Ts.ID = 1234
Ts.Name = "test"
Ts.password = "password"
Ts.Active = true

fmt.Println(structa)
fmt.Println(Ts)

c := make(chan int)
s := []int{7, 2, 8, 9}
Expand Down
Loading