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

[feat] support capture dash and zsh command #671

Closed
wants to merge 1 commit into from

Conversation

SenberHu
Copy link
Member

support dash and zsh command capture via ebpf u[ret]probe

@cfc4n
Copy link
Member

cfc4n commented Nov 21, 2024

@yuweizzz Could you review and test it?

@cfc4n cfc4n added enhancement New feature or request good first issue Good for newcomers labels Nov 21, 2024
@@ -0,0 +1,54 @@
// Copyright 2022 CFC4N <cfc4n.cs@gmail.com>. All Rights Reserved.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please disable compilation of the Android version, including other golang files involving dash/zsh.

//go:build !androidgki
// +build !androidgki


func init() {
dashCmd.PersistentFlags().StringVar(&dc.Dashpath, "dash", "", "$SHELL file path, eg: /bin/dash , will automatically find it from $ENV default.")
dashCmd.PersistentFlags().StringVar(&dc.Readline, "readlineso", "", "readline.so file path, will automatically find it from $BASH_PATH default.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the readlineso flag required? And, is the from $BASH_PATH default description wrong?


func init() {
zshCmd.PersistentFlags().StringVar(&zc.Zshpath, "zsh", "", "$SHELL file path, eg: /bin/zsh , will automatically find it from $ENV default.")
zshCmd.PersistentFlags().StringVar(&zc.Readline, "readlineso", "", "readline.so file path, will automatically find it from $BASH_PATH default.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

kern/dash_kern.c Outdated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that the kernel space code is not very different from bash_kern.c. Is it possible to reuse a copy of the bytecode? Is this possible by just setting different hook functions in the ebpf loader?

@ruitianzhong @sancppp What do you think?

func calReadFuncAddressByObjdump(elfPath string) (uint64, error) {
//0000000000005800 <read@plt>:
cmd := fmt.Sprintf("%s -d /bin/dash | grep read@plt | head -n 1", elfPath)
out, err := exec.Command("/bin/sh", "-c", cmd).Output()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing external commands is not a good practice, can it be achieved using debug/elf?

via:

goElf, err = elf.Open(gc.Path)
if err != nil {
return err
}
var goElfArch string
switch goElf.FileHeader.Machine.String() {
case elf.EM_AARCH64.String():
goElfArch = "arm64"
case elf.EM_X86_64.String():
goElfArch = "amd64"
default:
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":
default:
return fmt.Errorf("unsupport CPU arch :%s", goElfArch)
}
gc.goElfArch = goElfArch
gc.goElf = goElf
// If built with PIE and stripped, gopclntab is
// unlabeled and nested under .data.rel.ro.
for _, bs := range gc.Buildinfo.Settings {
if bs.Key == "-buildmode" {
if bs.Value == "pie" {
gc.IsPieBuildMode = true
}
break
}
}
if gc.IsPieBuildMode {
gc.goSymTab, err = gc.ReadTable()
if err != nil {
return err
}
var addr uint64
addr, err = gc.findPieSymbolAddr(GoTlsWriteFunc)
if err != nil {
return fmt.Errorf("%s symbol address error:%s", GoTlsWriteFunc, err.Error())
}
gc.GoTlsWriteAddr = addr
addr, err = gc.findPieSymbolAddr(GoTlsMasterSecretFunc)
if err != nil {
return fmt.Errorf("%s symbol address error:%s", GoTlsMasterSecretFunc, err.Error())
}
gc.GoTlsMasterSecretAddr = addr
gc.ReadTlsAddrs, err = gc.findRetOffsetsPie(GoTlsReadFunc)
if err != nil {
return err
}
} else {
gc.ReadTlsAddrs, err = gc.findRetOffsets(GoTlsReadFunc)
if err != nil {
return err
}
}


var readlineFuncName string // 将默认hook函数改为readline_internal_teardown说明:https://github.com/gojue/ecapture/pull/479
readlineFuncName = b.conf.(*config.DashConfig).ReadlineFuncName
addr, err := b.calReadFuncAddress()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addr should be a configuration item, passed in by the config.DashConfig structure, that is to say, the function of finding the offset address should be placed in the config package.

@yuweizzz
Copy link
Contributor

different behavior with bash, a single enter will trigger new event:

# zsh
debian# 
debian# 
debian# ls
systemd-private-1a9e37d51633440798de68a2bd4d8ad4-systemd-logind.service-o0lMZ2	systemd-private-1a9e37d51633440798de68a2bd4d8ad4-systemd-timesyncd.service-NHlMjG
debian# 
-----------------------------------------------------------------------------------
# ./ecapture zsh
2024-11-22T02:08:49Z INF AppName="eCapture(旁观者)"
2024-11-22T02:08:49Z INF HomePage=https://ecapture.cc
2024-11-22T02:08:49Z INF Repository=https://github.com/gojue/ecapture
2024-11-22T02:08:49Z INF Author="CFC4N <cfc4ncs@gmail.com>"
2024-11-22T02:08:49Z INF Description="Capturing SSL/TLS plaintext without a CA certificate using eBPF. Supported on Linux/Android kernels for amd64/arm64."
2024-11-22T02:08:49Z INF Version=linux_amd64:-20241121-e15bb70:6.1.0-22-amd64
2024-11-22T02:08:49Z INF Listen=localhost:28256
2024-11-22T02:08:49Z INF eCapture running logs logger=
2024-11-22T02:08:49Z INF the file handler that receives the captured event eventCollector=
2024-11-22T02:08:49Z WRN ========== module starting. ==========
2024-11-22T02:08:49Z INF Kernel Info=6.1.0 Pid=11161
2024-11-22T02:08:49Z INF BTF bytecode mode: CORE. btfMode=0
2024-11-22T02:08:49Z INF module initialization. isReload=false moduleName=EBPFProbeZsh
2024-11-22T02:08:49Z INF Module.Run()
2024-11-22T02:08:49Z INF BPF bytecode file is matched. bpfFileName=user/bytecode/zsh_kern_core.o
2024-11-22T02:08:49Z INF Hook Info binaryPath=/bin/zsh exec_builtin=zleentry execute_command=zleentry exit_builtin=zleentry readlineFuncName=zleentry
2024-11-22T02:08:49Z INF listen=localhost:28256
2024-11-22T02:08:49Z INF https server starting...You can update the configuration file via the HTTP interface.
2024-11-22T02:08:49Z INF target all process.
2024-11-22T02:08:49Z INF target all users.
2024-11-22T02:08:49Z INF perfEventReader created mapSize(MB)=4
2024-11-22T02:08:49Z INF module started successfully. isReload=false moduleName=EBPFProbeZsh
2024-11-22T02:08:53Z ??? PID:11169, UID:0, 	Comm:zsh, 	Line:
2024-11-22T02:08:53Z ??? PID:11169, UID:0, 	Comm:zsh, 	Line:
2024-11-22T02:08:56Z ??? PID:11169, UID:0, 	Comm:zsh, 	Line:
ls

ditto:

# dash
# ls
systemd-private-1a9e37d51633440798de68a2bd4d8ad4-systemd-logind.service-o0lMZ2	systemd-private-1a9e37d51633440798de68a2bd4d8ad4-systemd-timesyncd.service-NHlMjG
# 
# 
# 
# 
---------------------------------------------------------------------------------------------------
# ./ecapture dash
2024-11-22T02:06:31Z INF AppName="eCapture(旁观者)"
2024-11-22T02:06:31Z INF HomePage=https://ecapture.cc
2024-11-22T02:06:31Z INF Repository=https://github.com/gojue/ecapture
2024-11-22T02:06:31Z INF Author="CFC4N <cfc4ncs@gmail.com>"
2024-11-22T02:06:31Z INF Description="Capturing SSL/TLS plaintext without a CA certificate using eBPF. Supported on Linux/Android kernels for amd64/arm64."
2024-11-22T02:06:31Z INF Version=linux_amd64:-20241121-e15bb70:6.1.0-22-amd64
2024-11-22T02:06:31Z INF Listen=localhost:28256
2024-11-22T02:06:31Z INF eCapture running logs logger=
2024-11-22T02:06:31Z INF the file handler that receives the captured event eventCollector=
2024-11-22T02:06:31Z WRN ========== module starting. ==========
2024-11-22T02:06:31Z INF Kernel Info=6.1.0 Pid=11134
2024-11-22T02:06:31Z INF BTF bytecode mode: CORE. btfMode=0
2024-11-22T02:06:31Z INF module initialization. isReload=false moduleName=EBPFProbeDash
2024-11-22T02:06:31Z INF Module.Run()
2024-11-22T02:06:31Z INF listen=localhost:28256
2024-11-22T02:06:31Z INF https server starting...You can update the configuration file via the HTTP interface.
2024-11-22T02:06:31Z INF BPF bytecode file is matched. bpfFileName=user/bytecode/dash_kern_core.o
2024-11-22T02:06:31Z INF calc success
2024-11-22T02:06:31Z INF Hook Info binaryPath=/bin/dash exec_builtin=read execute_command=read exit_builtin=read readlineFuncName=read
2024-11-22T02:06:31Z INF target all process.
2024-11-22T02:06:31Z INF target all users.
2024-11-22T02:06:31Z INF perfEventReader created mapSize(MB)=4
2024-11-22T02:06:31Z INF module started successfully. isReload=false moduleName=EBPFProbeDash
2024-11-22T02:06:34Z ??? PID:11146, UID:0, 	Comm:dash, 	Line:

2024-11-22T02:06:35Z ??? PID:11146, UID:0, 	Comm:dash, 	Line:
ls

2024-11-22T02:06:37Z ??? PID:11146, UID:0, 	Comm:dash, 	Line:

s

2024-11-22T02:06:37Z ??? PID:11146, UID:0, 	Comm:dash, 	Line:

s

2024-11-22T02:06:37Z ??? PID:11146, UID:0, 	Comm:dash, 	Line:

s

@SenberHu SenberHu closed this Dec 5, 2024
@SenberHu
Copy link
Member Author

SenberHu commented Dec 5, 2024

dash源码中,当前考虑通过probe libc库中read函数进行命令行读取,
该函数为plt函数,需要获取相对偏移量,原pr中借用objdump或gdb进行偏移量确认
在尝试用go解析偏移量时,遇到了反汇编text段后,无法准确识别到read@plt问题

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants