Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mandiant/GoReSym
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.6.2
Choose a base ref
...
head repository: mandiant/GoReSym
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Oct 12, 2023

  1. Copy the full SHA
    6d33cb4 View commit details
  2. Fix basic type aliases

    stevemk14ebr committed Oct 12, 2023
    Copy the full SHA
    ce1e752 View commit details
  3. Copy the full SHA
    5764f92 View commit details
  4. Import types into IDA

    stevemk14ebr committed Oct 12, 2023
    Copy the full SHA
    41c8b47 View commit details
  5. Copy the full SHA
    5f3a1ad View commit details

Commits on Oct 13, 2023

  1. Copy the full SHA
    fd75e4c View commit details

Commits on Nov 10, 2023

  1. Copy the full SHA
    7e26237 View commit details
  2. Resolve #42

    stevemk14ebr committed Nov 10, 2023
    Copy the full SHA
    e867149 View commit details

Commits on Nov 27, 2023

  1. Update README.md

    stevemk14ebr authored Nov 27, 2023
    Copy the full SHA
    e1627a7 View commit details
  2. Update README.md

    stevemk14ebr authored Nov 27, 2023
    Copy the full SHA
    c7be20e View commit details

Commits on Nov 28, 2023

  1. Copy the full SHA
    7c3a6bb View commit details
  2. Copy the full SHA
    d75fbb4 View commit details

Commits on Feb 8, 2024

  1. Copy the full SHA
    a2567ab View commit details
  2. Copy the full SHA
    9307a5e View commit details
  3. Rollback CI version

    stevemk14ebr committed Feb 8, 2024
    Copy the full SHA
    7918cd1 View commit details
  4. Copy the full SHA
    5e8c513 View commit details
  5. Update README.md

    stevemk14ebr authored Feb 8, 2024
    Copy the full SHA
    9c9af9a View commit details
  6. Update README.md

    stevemk14ebr authored Feb 8, 2024
    Copy the full SHA
    0860a1b View commit details

Commits on Feb 23, 2024

  1. Fix attempt logic in moduledata scan to ignore erroneous early hits a…

    …nd add them to the ignorelist, resolves #50
    stevemk14ebr committed Feb 23, 2024
    Copy the full SHA
    76f8f7c View commit details

Commits on Apr 15, 2024

  1. Copy the full SHA
    80893bd View commit details
  2. Update README.md

    stevemk14ebr authored Apr 15, 2024
    Copy the full SHA
    7c37bd7 View commit details

Commits on May 7, 2024

  1. Copy the full SHA
    6e65730 View commit details
  2. Fix workflow

    stevemk14ebr authored May 7, 2024
    Copy the full SHA
    cc91ae7 View commit details

Commits on May 30, 2024

  1. Copy the full SHA
    62070ed View commit details

Commits on Jun 10, 2024

  1. Copy the full SHA
    0d5caca View commit details
  2. Copy the full SHA
    d0f297e View commit details
  3. Copy the full SHA
    82f4cfd View commit details
  4. Copy the full SHA
    5398a6a View commit details
  5. Copy the full SHA
    8df84f7 View commit details
  6. Copy the full SHA
    fce172d View commit details
  7. Copy the full SHA
    128f46b View commit details

Commits on Aug 2, 2024

  1. Optionally skip printing the function sections (#60)

    * Optionally skip printing the function sections
    
    Add a new -no-functions command line flag to skip printing the
    UserFunctions and StdFunctions detailed sections.
    This is useful to get a quick summary of what is in a Go binary.
    The default is to always print the function sections.
    
    Reference: #49
    Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
    
    * Optionally skip printing the function sections
    
    Use -nofuncs command line flag instead.
    Reorder main_impl() function arguments.
    
    Reference: #49
    Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
    
    ---------
    
    Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
    pombredanne authored Aug 2, 2024
    Copy the full SHA
    e3bc188 View commit details

Commits on Aug 16, 2024

  1. Copy the full SHA
    c25fb12 View commit details

Commits on Aug 19, 2024

  1. Copy the full SHA
    82e128d View commit details
  2. More fixes and improvements to pattern matching (#64)

    * Support pattern negated byte
    
    * Optimize pattern sub-matches
    
    * Fix one-off pattern mismatch
    
    * Fix test
    
    * Fix test
    
    * Revert "Optimize pattern sub-matches"
    
    This reverts commit 6f4badc.
    
    * Simplify data_end calculation
    
    ---------
    
    Co-authored-by: Stephen Eckels <stepheneckels@google.com>
    Co-authored-by: Stephen Eckels <stevemk14ebr@gmail.com>
    3 people authored Aug 19, 2024
    Copy the full SHA
    d7d9a98 View commit details
  3. Copy the full SHA
    784db2d View commit details
  4. Copy the full SHA
    534ca84 View commit details

Commits on Oct 19, 2024

  1. Copy the full SHA
    54a6712 View commit details
  2. Optimize and omit duplicate pattern matches (#66) (#68)

    Co-authored-by: ViRb3 <ViRb3@users.noreply.github.com>
    stevemk14ebr and ViRb3 authored Oct 19, 2024
    Copy the full SHA
    45d5c9d View commit details

Commits on Feb 12, 2025

  1. Fix tests

    stevemk14ebr committed Feb 12, 2025
    Copy the full SHA
    a6bdd54 View commit details

Commits on Feb 21, 2025

  1. Fix argc bug

    stevemk14ebr committed Feb 21, 2025
    Copy the full SHA
    0125e4c View commit details
Showing with 1,486 additions and 760 deletions.
  1. +27 −10 .github/workflows/main.yaml
  2. +6 −6 GhidraPython/goresym_rename.py
  3. +85 −11 IDAPython/goresym_rename.py
  4. +4 −4 README.md
  5. +18 −0 archive/archive.go
  6. +1 −1 build_test_files.sh
  7. +85 −54 buildinfo/buildinfo.go
  8. +39 −24 main.go
  9. +4 −4 main_test.go
  10. +5 −2 objfile/disasm.go
  11. +149 −144 objfile/elf.go
  12. +3 −3 objfile/goobj.go
  13. +120 −0 objfile/internals.go
  14. +141 −142 objfile/macho.go
  15. +291 −89 objfile/objfile.go
  16. +134 −54 objfile/patterns.go
  17. +121 −1 objfile/patterns_test.go
  18. +143 −148 objfile/pe.go
  19. +6 −7 objfile/scanner.go
  20. +104 −56 runtime/debug/mod.go
37 changes: 27 additions & 10 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
@@ -12,19 +12,36 @@ jobs:
with:
go-version: '>=1.21.0'
- run: go version
- name: GoReSym Build
- name: Windows Build
run: |
$Env:GOOS='linux'
go build -o GoReSym_lin
$Env:GOOS='windows'
go build -o GoReSym_win.exe
$Env:GOOS='darwin'
go build -o GoReSym_mac
- name: Zip Build
go build -o GoReSym.exe
Compress-Archive -DestinationPath GoReSym-windows.zip -LiteralPath ./GoReSym.exe -CompressionLevel Fastest
Remove-Item ./GoReSym.exe
- name: Linux Build
run: |
$Env:GOOS='linux'
go build -o GoReSym
Compress-Archive -DestinationPath GoReSym-linux.zip -LiteralPath ./GoReSym -CompressionLevel Fastest
Remove-Item ./GoReSym
- name: Mac Build
run: |
Compress-Archive -DestinationPath GoReSym.zip -LiteralPath ./GoReSym_lin, ./GoReSym_win.exe, ./GoReSym_mac -CompressionLevel Fastest
- name: Release
$Env:GOOS='darwin'
go build -o GoReSym
Compress-Archive -DestinationPath GoReSym-mac.zip -LiteralPath ./GoReSym -CompressionLevel Fastest
Remove-Item ./GoReSym
- name: Release Windows
uses: softprops/action-gh-release@v0.1.12
if: startsWith(github.ref, 'refs/tags/')
with:
files: GoReSym-windows.zip
- name: Release Linux
uses: softprops/action-gh-release@v0.1.12
if: startsWith(github.ref, 'refs/tags/')
with:
files: GoReSym-linux.zip
- name: Release Mac
uses: softprops/action-gh-release@v0.1.12
if: startsWith(github.ref, 'refs/tags/')
with:
files: GoReSym.zip
files: GoReSym-mac.zip
12 changes: 6 additions & 6 deletions GhidraPython/goresym_rename.py
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ def _entry_addr_estimator(hints):

func = max(candidates, key=lambda f: len(f["FullName"]))

fm = currentProgram.getFunctionManager()
fm = getCurrentProgram().getFunctionManager()
for f in fm.getFunctions(True):
if f.getName() == "entry":
return f.getEntryPoint().getOffset()-func["Start"]
@@ -63,7 +63,7 @@ def _entry_addr_estimator(hints):

def _pclntab_estimator(hints):
# Expected pclntab name per exec format
exec_fmt = currentProgram.getMetadata()["Executable Format"]
exec_fmt = getCurrentProgram().getMetadata()["Executable Format"]
pclntabs = []
block_name = ".text"
if exec_fmt == "Executable and Linking Format (ELF)":
@@ -85,11 +85,11 @@ def _pclntab_estimator(hints):

# Check if pclntab is present
for p in pclntabs:
pclntab = currentProgram.getMemory().getBlock(p)
pclntab = getCurrentProgram().getMemory().getBlock(p)
if pclntab is not None:
# With pclntab, offset should be a matter of TabMeta VA
tmva = hints["TabMeta"]["VA"]
text = currentProgram.getMemory().getBlock(block_name)
text = getCurrentProgram().getMemory().getBlock(block_name)
return text.getStart().getOffset()-tmva

return None
@@ -101,7 +101,7 @@ def _func_map_estimator(hints):
grs_funcs = {f["FullName"]: f for f in hfuncs}

# Match function lists and collect possible offsets
fm = currentProgram.getFunctionManager()
fm = getCurrentProgram().getFunctionManager()
for f in fm.getFunctions(True):
name = f.getName()
if "FUN_" in name or name == "entry":
@@ -140,7 +140,7 @@ def rename_funcs(items, offset, simulate=False):
if not iterable(items):
return 0, 0

fm = currentProgram.getFunctionManager()
fm = getCurrentProgram().getFunctionManager()
rename_counter, create_counter = 0, 0
for func in items:
try:
96 changes: 85 additions & 11 deletions IDAPython/goresym_rename.py
Original file line number Diff line number Diff line change
@@ -18,6 +18,72 @@ def iterable(obj):
else:
return True

# https://gist.github.com/NyaMisty/693db2ce2e75c230f36b628fd7610852
# 'Synchonize to idb' right click equivalent
def resync_local_types():
def is_autosync(name, tif):
return idaapi.get_ordinal_from_idb_type(name, tif.get_decltype().to_bytes(1, 'little')) != -1

for ord in range(1, idaapi.get_ordinal_qty(None)):
t = idaapi.tinfo_t()
t.get_numbered_type(None, ord)
typename = t.get_type_name()
if typename.startswith('#'):
continue

autosync = is_autosync(typename, t)
#print('Processing struct %d: %s%s' % (ord, typename, ' (autosync) ' if autosync else ''))
idaapi.import_type(None, -1, typename, idaapi.IMPTYPE_OVERRIDE)
if autosync:
continue
struc = idaapi.get_struc(idaapi.get_struc_id(typename))
if not struc:
continue
struc.ordinal = -1
idaapi.save_struc(struc, False)

def get_type_by_name(name):
t = idaapi.tinfo_t()
t.get_named_type(None, name)
return t

def set_function_signature(ea, typedef):
idaapi.apply_type(ea, ida_typeinf.parse_decl(typedef, ida_typeinf.PT_SIL), idaapi.TINFO_DEFINITE)

def import_primitives():
type_map = {
"BUILTIN_STRING": "string",
"uint8_t": "uint8",
"uint16_t": "uint16",
"uint32_t": "uint32",
"uint64_t": "uint64",
"int8_t": "int8",
"int16_t": "int16",
"int32_t": "int32",
"double": "float64",
"float": "float32",
"complex64_t": "complex64",
"complex128_t": "complex128",
"void*": "uintptr", # should be uint64 or uint32 depending on ptr size, but this works too
"uint8": "byte",
"int32": "rune",
"int": "void*" # int in GO depends on architecture size
}

ida_typeinf.idc_parse_types("struct BUILTIN_INTERFACE{void *tab;void *data;};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
ida_typeinf.idc_parse_types("struct BUILTIN_STRING{char *ptr;size_t len;};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)

ida_typeinf.idc_parse_types("struct complex64_t{float real;float imag;};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
ida_typeinf.idc_parse_types("struct complex128_t{double real;double imag;};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)

for ida_type, gotype in type_map.items():
ida_typeinf.idc_parse_types(f"typedef {ida_type} {gotype};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)

def forward_declare_structs(types):
for typ in types:
if typ['Kind'] == 'Struct':
ida_typeinf.idc_parse_types(f"struct {typ['CStr']};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)

hints = ida_kernwin.ask_file(0, "*.*", "GoReSym output file")
with open(hints, "r", encoding="utf-8") as rp:
buf = rp.read()
@@ -40,21 +106,29 @@ def iterable(obj):
idaapi.set_name(func['Start'], func['FullName'], idaapi.SN_NOWARN | idaapi.SN_NOCHECK | ida_name.SN_FORCE)

if iterable(hints['Types']):
## import all the types first (reverse order, so that child types imported first)
#for typ in hints['Types'][::-1]:
# if typ.get('CReconstructed'):
# errors = ida_typeinf.idc_parse_types(typ['CReconstructed'] + ";", ida_typeinf.HTI_PAKDEF)
# if errors > 0:
# print(typ['CReconstructed'], "failed to import")

import_primitives()

# we must do this to prevent IDA from creating an invalid struct of type int when we import things like typedef <class>* <newname>.
# it would have made typedef struct <class> int; without a forward declaration. That would then break importing the class later with redefinition error.
forward_declare_structs(hints['Types'])

for typ in hints['Types'][::-1]:
if typ.get('CReconstructed'):
errors = ida_typeinf.idc_parse_types(typ['CReconstructed'] + ";", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
if errors > 0:
print(typ['CReconstructed'], "failed to import")

# just for precation
resync_local_types()

for typ in hints['Types']:
print("Renaming %s to %s" % (hex(typ['VA']), typ['Str']))
idaapi.set_name(typ['VA'], typ['Str'], idaapi.SN_NOWARN | idaapi.SN_NOCHECK | ida_name.SN_FORCE)

# IDA often thinks these are string pointers, lets undefine that, then set the type correctly
ida_bytes.del_items(typ['VA'], 0, 4)
py_type = idaapi.idc_parse_decl(idaapi.cvar.idati, "void* ptr;", 1)
idaapi.apply_type(idaapi.cvar.idati, py_type[1], py_type[2], typ['VA'], idaapi.TINFO_DEFINITE)
abi_typ = get_type_by_name("abi_Type")
idaapi.apply_tinfo(typ['VA'], abi_typ, idaapi.TINFO_DEFINITE)

if iterable(hints['Interfaces']):
for typ in hints['Interfaces']:
@@ -63,8 +137,8 @@ def iterable(obj):

# IDA often thinks these are string pointers, lets undefine that, then set the type correctly
ida_bytes.del_items(typ['VA'], 0, 4)
py_type = idaapi.idc_parse_decl(idaapi.cvar.idati, "void* ptr;", 1)
idaapi.apply_type(idaapi.cvar.idati, py_type[1], py_type[2], typ['VA'], idaapi.TINFO_DEFINITE)
abi_typ = get_type_by_name("abi_Type")
idaapi.apply_tinfo(typ['VA'], abi_typ, idaapi.TINFO_DEFINITE)

if hints['TabMeta'] is not None:
tabmeta = hints['TabMeta']
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ The upstream Go runtime code is extended to handle:
# Usage
Refer to https://www.mandiant.com/resources/blog/golang-internals-symbol-recovery for reverse engineering details and example usage.

You can download pre-built `linux` and `windows` GoReSym binaries from the [Releases tab](https://github.com/mandiant/GoReSym/releases/).
You can download pre-built `linux`, `macos`, and `windows` GoReSym binaries from the [Releases tab](https://github.com/mandiant/GoReSym/releases/).

To build from source with a recent Go compiler, invoke the Go compiler:

@@ -74,11 +74,11 @@ Here are all the available flags:
* `-m <virtual address>` ("manual", optional) flag will dump the `RTYPE` structure recursively at the given virtual address
* `-v <version string>` ("version", optional) flag will override automated version detection and use the provided version. This is needed for some stripped binaries. Type parsing will fail if the version is not accurate.
* `-human` (optional) flag will print a flat text listing instead of JSON. Especially useful when printing structure and interface types.

* `-about` (optional) flag with print out license information

To import this information into IDA Pro you can run the script found in [https://github.com/mandiant/GoReSym/blob/master/IDAPython/goresym_rename.py](IDAPython/goresym_rename.py). It will read a json file produced by GoReSym and set symbols/labels in IDA.

# Version Support

As the Go compiler and runtime have changed, so have the embedded metadata structures. GoReSym supports the following combinations of Go releases & metadata:

* all combinations of ARM64 𝒙 Intel x86/x64 𝒙 MACH-O/ELF/PE 𝒙 big/little endian
@@ -88,7 +88,7 @@ As the Go compiler and runtime have changed, so have the embedded metadata struc

The `moduledata` table used to extract types doesn't exist prior to Go 1.5, so this library will never support extracting types from very old Go versions.

This library current handles legacy `pclntab` (pre Go 1.2), 1.2, 1.16, 1.18, 1.19, and 1.20.
This library current handles the `pclntab` layouts pre 1.2, 1.2, 1.16, 1.18, and 1.20. Note that the pclntab version is always <= the Go runtime version (ex: Go runtime 1.19 uses the 1.18 pclntab layout), we aim to _always support the latest runtime versions_.

# Contributions
Much of the source code from GoReSym is copied from the upstream Go compiler source directory `/internal`. To make this work, we've had to massage the source a bit. If you want to contribute to GoReSym, read on so we can explain this import process.
18 changes: 18 additions & 0 deletions archive/archive.go
Original file line number Diff line number Diff line change
@@ -73,6 +73,7 @@ const (
EntryPkgDef EntryType = iota
EntryGoObj
EntryNativeObj
EntrySentinelNonObj
)

func (e *Entry) String() string {
@@ -354,6 +355,23 @@ func (r *objReader) parseArchive(verbose bool) error {
Data: Data{r.offset, size},
})
r.skip(size)
case "preferlinkext", "dynimportfail":
if size == 0 {
// These are not actual objects, but rather sentinel
// entries put into the archive by the Go command to
// be read by the linker. See #62036.
r.a.Entries = append(r.a.Entries, Entry{
Name: name,
Type: EntrySentinelNonObj,
Mtime: mtime,
Uid: uid,
Gid: gid,
Mode: mode,
Data: Data{r.offset, size},
})
break
}
fallthrough
default:
var typ EntryType
var o *GoObj
2 changes: 1 addition & 1 deletion build_test_files.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
trap "exit" INT
sudo rm -rf $(pwd)/test/build
versions=("1.21" "1.20" "1.19" "1.18" "1.17" "1.16" "1.15" "1.14" "1.13" "1.12" "1.11" "1.10" "1.9" "1.8" "1.7" "1.6" "1.5")
versions=("1.22" "1.21" "1.20" "1.19" "1.18" "1.17" "1.16" "1.15" "1.14" "1.13" "1.12" "1.11" "1.10" "1.9" "1.8" "1.7" "1.6" "1.5")
for v in "${versions[@]}"
do
GO_TAG=$v
Loading