Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] gotls function offset #1

Closed
wants to merge 2 commits into from
Closed
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
72 changes: 72 additions & 0 deletions tests/gotls/elf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

import (
"ecapture/user/config"
"testing"
)

func Test_GOTLSConfig(t *testing.T) {
check := func(t *testing.T, path string, want []int, isPie bool) {
gotls := config.NewGoTLSConfig()
gotls.Path = path
gotls.Check() // testcase contain arm64 and amd64, decode instruction will return error

if gotls.IsPieBuildMode != isPie {
t.Fatalf("this testcase build error! gotPIE(%v) vs wantPIE(%v)", gotls.IsPieBuildMode, isPie)
}
got := gotls.ReadTlsAddrs // function: "crypto/tls.(*Conn).Read"

if len(got) != len(want) {
t.Fatalf("leng error: want(%d) vs got(%d)", len(want), len(got))
}
for i, g := range got {
if want[i] != g {
t.Fatalf("item[%d]: want(%d) vs got(%d)", i, want[i], g)
}
}
}

t.Run("amd64 default", func(t *testing.T) {
// go build -o ./amd64_default/main ./main.go
path := "./testdata/amd64_default/main"
check(t, path, []int{
// this data get form cli: `./testdata/get_ret.sh ./testdata/normal_x86/main`
0x1a47b3, 0x1a47da, 0x1a4856, 0x1a49ab, 0x1a49da, 0x1a4a49, 0x1a4a63,
}, false)
})

t.Run("amd64 pie", func(t *testing.T) {
// go build -buildmode=pie -o ./amd64_default/main ./main.go
path := "./testdata/amd64_pie/main"
check(t, path, []int{0x1a4e13, 0x1a4e3a, 0x1a4eb6, 0x1a500b, 0x1a503a, 0x1a50a9, 0x1a50c3}, true)
})

t.Run("ubuntu 22.04 default", func(t *testing.T) {
// in docker ubuntu 22.04
check(t, "./testdata/ubuntu_default/main", []int{
0x19fbb3, 0x19fbda, 0x19fc56, 0x19fdab, 0x19fdda, 0x19fe49, 0x19fe63,
}, false)
})

t.Run("ubuntu 22.04 pie", func(t *testing.T) {
// in docker ubuntu 22.04
check(t, "./testdata/ubuntu_pie/main", []int{
0x1a0213, 0x1a023a, 0x1a02b6, 0x1a040b, 0x1a043a, 0x1a04a9, 0x1a04c3,
}, true)
})

t.Run("arm64 default", func(t *testing.T) {
// GOOS=linux GOARCH=arm64 go build -o ./arm64_pie/main ./main.go
check(t, "./testdata/arm64_default/main", []int{
0x171510, 0x171538, 0x17159c, 0x1716b8, 0x1716e8, 0x171748, 0x171764,
}, false)
})

t.Run("arm64 pie", func(t *testing.T) {
// GOOS=linux GOARCH=arm64 go build -buildmode=pie -o ./arm64_pie/main ./main.go
check(t, "./testdata/arm64_pie/main", []int{
0x171510, 0x171538, 0x17159c, 0x1716b8, 0x1716e8, 0x171748, 0x171764,
}, true)
})

}
Binary file added tests/gotls/testdata/amd64_default/main
Binary file not shown.
Binary file added tests/gotls/testdata/amd64_pie/main
Binary file not shown.
Binary file added tests/gotls/testdata/arm64_default/main
Binary file not shown.
Binary file added tests/gotls/testdata/arm64_pie/main
Binary file not shown.
71 changes: 71 additions & 0 deletions tests/gotls/testdata/get_ret.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/bin/bash
OUTPUT_PATH=$1
FUNCTION="crypto/tls.(\*Conn).Read$"


# Use readelf to get function details
# readelf -s (display the symbol table), -W(Allow output width to exceed 80 characters )
FUNCTION_DETAILS=$(readelf -sW $OUTPUT_PATH | grep $FUNCTION)
# this data look like this:
# `echo $FUNCTION_DETALIS`
# 4335: 000000000018c2e0 928 FUNC GLOBAL DEFAULT 1 crypto/tls.(*Conn).Read
# offset(base 16) size(base 10)

# about this `awk`
# echo $FUNCTION_DETAILS | awk '{for(i=1; i<=NF; i++) print $i}'
# 4335:
# 000000000018c2e0
# 928
# FUNC
# GLOBAL
# DEFAULT
# 1
# crypto/tls.(*Conn).Read
FUNCTION_START=$(echo $FUNCTION_DETAILS | awk '{print $2}')
FUNCTION_SIZE=$(echo $FUNCTION_DETAILS | awk '{print $3}')

# Convert the hexadecimal values to decimal
FUNCTION_START_DEC=$(printf "%d" 0x$FUNCTION_START)
FUNCTION_SIZE_DEC=$(printf "%d" $FUNCTION_SIZE)

# Calculate the end address
FUNCTION_END_DEC=$((FUNCTION_START_DEC + FUNCTION_SIZE_DEC))

# Convert the decimal end address back to hexadecimal
FUNCTION_END=$(printf "%x" $FUNCTION_END_DEC)

# echo "Function Start: $FUNCTION_START"
# echo "Function End: $FUNCTION_END"


# Disassemble the function
DISASM=$(objdump -d -j .text --start-address=0x$FUNCTION_START --stop-address=0x$FUNCTION_END $OUTPUT_PATH)

# Get the start address and file offset of the .text section
SECTION_START=$(readelf -W -S $OUTPUT_PATH | awk '/.text/ {print "0x"$5}')
SECTION_OFFSET=$(readelf -W -S $OUTPUT_PATH | awk '/.text/ {print "0x"$6}')

# echo "Section Start: $SECTION_START"
# echo "Section Offset: $SECTION_OFFSET"


# Get the virtual addresses of the "ret" instructions
RET_ADDRESSES=$(echo "$DISASM" | grep "\sret" | awk '{print $1}' | sed 's/://g')

# Convert the virtual addresses to file offsets
for ADDR in $RET_ADDRESSES; do
# Convert the address to decimal
ADDR_DEC=$(printf "%d" 0x$ADDR)

# Convert the start address and file offset to decimal
SECTION_START_DEC=$(printf "%d" $SECTION_START)
SECTION_OFFSET_DEC=$(printf "%d" $SECTION_OFFSET)

# Calculate the file offset of the address
OFFSET_DEC=$((ADDR_DEC - SECTION_START_DEC + SECTION_OFFSET_DEC))

# Convert the file offset to hexadecimal
OFFSET=$(printf "%x" $OFFSET_DEC)

echo -n "0x$OFFSET, "
done
31 changes: 31 additions & 0 deletions tests/gotls/testdata/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"fmt"
"io"
"log"
"net/http"
)

/*
Test File

you can use `go build` this file
with ARCH(amd64/arm64) and BUILDMODE(normal/pie)

`GOOS=(linux/android) GOARCH=(amd64/arm64) go build -buildmode=xxx -o xxx/main main.go`

when build script runing:
you will see function `"crypto/tls.(\*Conn).Read$"` address
and offset address of return instruction of this function.
this offset will be useful in test case.
*/
func main() {
rsp, err := http.Get("https://com.example.com")
if err != nil {
log.Fatal("http.Get error: ", err)
}
defer rsp.Body.Close()
body, _ := io.ReadAll(rsp.Body)
fmt.Printf("http.Get: \n\t%s\n", body)
}
Binary file added tests/gotls/testdata/ubuntu_default/main
Binary file not shown.
Binary file added tests/gotls/testdata/ubuntu_pie/main
Binary file not shown.
7 changes: 1 addition & 6 deletions user/config/config_gotls.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"errors"
"fmt"
"os"
"runtime"
"strings"
)

Expand Down Expand Up @@ -141,10 +140,6 @@ func (gc *GoTLSConfig) Check() error {
goElfArch = "unsupport_arch"
}

if goElfArch != runtime.GOARCH {
err = fmt.Errorf("Go Application not match, want:%s, have:%s", runtime.GOARCH, goElfArch)
return err
}
switch goElfArch {
case "amd64":
case "arm64":
Expand Down Expand Up @@ -366,7 +361,7 @@ func (gc *GoTLSConfig) findPieSymbolAddr(lfunc string) (uint64, error) {
if prog.Vaddr <= f.Value && f.Value < (prog.Vaddr+prog.Memsz) {
funcLen := f.End - f.Entry
data := make([]byte, funcLen)
address := f.Value - prog.Vaddr + prog.Off + IdaProOffset
address := f.Value - prog.Vaddr + prog.Off
_, err = prog.ReadAt(data, int64(address))
if err != nil {
return 0, fmt.Errorf("search function return: %w", err)
Expand Down
53 changes: 53 additions & 0 deletions user/config/go_instructions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package config

import (
"fmt"

"golang.org/x/arch/arm64/arm64asm"
"golang.org/x/arch/x86/x86asm"
)

func (gc *GoTLSConfig) decodeInstruction(instHex []byte) ([]int, error) {
switch gc.goElfArch {
case "amd64":
return decodeInstructionAMD(instHex)
case "arm64":
return decodeInstructionARM(instHex)
default:
return nil, fmt.Errorf("unsupport CPU arch :%s", gc.goElfArch)
}
}

// decodeInstruction Decode into assembly instructions and identify the RET instruction to return the offset.
func decodeInstructionAMD(instHex []byte) ([]int, error) {
var offsets []int
for i := 0; i < len(instHex); {
inst, err := x86asm.Decode(instHex[i:], 64)
if err != nil {
return nil, err
}
if inst.Op == x86asm.RET {
offsets = append(offsets, i)
}
i += inst.Len
}
return offsets, nil
}

const (
// Arm64armInstSize via : arm64/arm64asm/decode.go:Decode() size = 4
Arm64armInstSize = 4
)

// decodeInstruction Decode into assembly instructions and identify the RET instruction to return the offset.
func decodeInstructionARM(instHex []byte) ([]int, error) {
var offsets []int
for i := 0; i < len(instHex); {
inst, _ := arm64asm.Decode(instHex[i:]) // Why ignore error: https://github.com/gojue/ecapture/pull/506
if inst.Op == arm64asm.RET {
offsets = append(offsets, i)
}
i += Arm64armInstSize
}
return offsets, nil
}
21 changes: 0 additions & 21 deletions user/config/go_instructions_amd64.go

This file was deleted.

23 changes: 0 additions & 23 deletions user/config/go_instructions_arm64.go

This file was deleted.