如何 embed binary 到 shell script 中並執行取得結果


Posted by desmondshih on 2023-08-31

github repo for sample: https://github.com/desmondshih/embedtoshell

前言

工作中遇到一個需求是需要在 linux 套件(deb, rpm)中的 pre/post, install/rm 執行比較複雜性的檢查,而這個檢查是由 golang 撰寫.並且希望此 binary 不要被解到環境上,以下是完成這個目標所記錄的紀錄和練習.

完成目標中主要分為兩個部分

  • 如何把 binary embed 到 shell script 中
  • 如何執行 embeded binary in memory 並且取得結果回傳值

如何把 binary embed 到 shell script 中

  • 先做一個 binary, 這邊使用 golang 寫一個簡單的例子, 接收 args integer 方便指定 return code 方便後續測試
package main

import (
    "fmt"
    "os"
    "strconv"
)

func main() {
    var rc int
    if len(os.Args) == 2 {
        r, err := strconv.Atoi(os.Args[1])
        if err != nil {
            panic("args should be integer for return code, err: %s")
        }
        rc = r
    } else {
        rc = 0
    }
    fmt.Println("Hello go bin")
    os.Exit(rc)
}
-> % go build ./... && ls  # check go output embedtoshell
embed_binary_test.sh  embeded_binary_output  embedtoshell  go.mod  main.go

-> % ./embedtoshell
Hello go bin
-> % echo $?
0

-> % ./embedtoshell 87
Hello go bin

-> % echo $?
87
  • 在 shell script 中放置 binary, 建立一個測試 script "embed_binary_test.sh"
#!/bin/bash

sed "1,/^\#\#\/\*\*__PAYLOAD_BEGINS__\*\*\/\#\#$/ d" $0 | base64 -d | gzip -d -c > embed_binary_test_output

exit 0
##/**__PAYLOAD_BEGINS__**/##
  • 這邊 dump to raw string 使用 gzip 壓縮並存進 script tag "PAYLOAD_BEGIN" 位置後面
gzip -9 -c < embedtoshell | base64 >> embed_binary_test.sh
  • 執行 script: 會把前一步驟 dump string 讀取解壓然後存進 file "embeded_binary_output", 再來檢查內容,並執行來驗證正確性
-> % ./embed_binary_test.sh
-> % ls -lh
total 5.3M
-rwxrwxr-x 1 desmond desmond 1.4M Aug 15 10:30 embed_binary_test.sh
-rw-rw-r-- 1 desmond desmond 2.0M Aug 15 16:39 embed_binary_test_output
-rwxrwxr-x 1 desmond desmond 2.0M Aug 15 16:31 embedtoshell
-rw-rw-r-- 1 desmond desmond   29 Aug  7 15:31 go.mod
-rw-rw-r-- 1 desmond desmond  294 Aug 15 10:11 main.go

-> % md5sum embedtoshell embed_binary_test_output
b34d1c4cbb965ae26949695ccfc59b99  embedtoshell
b34d1c4cbb965ae26949695ccfc59b99  embed_binary_test_output

-> % chmod +x embed_binary_test_output
-> % ./embed_binary_test_output
Hello go bin

如何執行 embeded binary in memory 並且取得結果回傳值

  • 因為這次的 task 是要在 package preinstall 做一些操作, 不希望在這個階段產生檔案在環境中.所以這邊決定使用 linux memfd 的方式 run in memory
  • 因為 bash 上沒辦法直接做 memfd 相關操作,這邊用 perl & python 分別做的例子
  • perl sample
#!/bin/bash

unset POSIXLY_CORRECT
run_payload() {
   read -r pid mfd < <(perl -e '
        require qw/syscall.ph/;

        # Create memfd
        my $name = "";
        my $fd = syscall(SYS_memfd_create(), $fn, 0);
        if (-1 == $fd) { die "memfd_create: $!"; }

        $pid = fork();
        if ( $pid == 0 ) {
            sleep();
        }

        print "$pid $fd\n";
        select()->flush();
    ')
    memfd=/proc/$pid/fd/$mfd

    sed "1,/^\#\#\/\*\*__PAYLOAD_BEGINS__\*\*\/\#\#$/ d" $0 | base64 -d | gzip -d -c > ${memfd}

    ${memfd} 87
    PAYLOAD_RC=$?

    kill ${pid}

}

run_payload
echo "run elf string in memfd and get return code:" $PAYLOAD_RC

exit
##/**__PAYLOAD_BEGINS__**/##
  • python sample
#!/bin/bash

unset POSIXLY_CORRECT
run_payload() {
    ARG1=$1

    read -r pid mfd < <(python3 -c '
import os
import sys
import signal

fd = os.memfd_create("bin", 0)
if fd < 0:
    sys.exit(-1)

pid = os.fork()
if pid == 0:
    signal.pause()

print("%d %d" % (pid, fd))
sys.stdout.flush()
    ')
    if ! [[ ${pid} =~ ^[0-9]+$ ]] || ! [[ ${mfd} =~ ^[0-9]+$ ]]; then
        PAYLOAD_RC=1
        return
    fi

    memfd=/proc/$pid/fd/$mfd

    sed "1,/^\#\#\/\*\*__PAYLOAD_BEGINS__\*\*\/\#\#$/ d" $0 | base64 -d | gzip -d -c > ${memfd}

    ${memfd} 87
    PAYLOAD_RC=$?

    kill ${pid}
}

run_payload
echo "run elf string in memfd and get return code:" $PAYLOAD_RC

exit
##/**__PAYLOAD_BEGINS__**/##
  • 測試 script
-> % gzip -9 -c < embedtoshell | base64 >> embed_binary_perl.sh
-> % ./embed_binary_perl.sh
Hello go bin
run elf string in memfd and get return code: 87

-> % gzip -9 -c < embedtoshell | base64 >> embed_binary_python.sh
-> % ./embed_binary_python.sh
Hello go bin
run elf string in memfd and get return code: 87

References


#linux #os #system #shell #Memfd #elf







Related Posts

[前端工具] CSS 預處理器:SCSS

[前端工具] CSS 預處理器:SCSS

Day05_Origami學習筆記

Day05_Origami學習筆記

MTR04_0710

MTR04_0710


Comments