mirror of
https://github.com/rizinorg/rz-silhouette-server.git
synced 2026-11-07 06:55:57 -10:00
capnproto migration from protobuf (#4)
This commit is contained in:
1 parent
a9e2c76254
commit
701b75f689
26 files changed
+2183
-1548
No files matched your search
@@ -110,8 +110,21 @@ jobs:
|
||||
with:
|
||||
go-version: ^1.24
|
||||
|
||||
- name: Install Cap'n Proto compiler
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y capnproto
|
||||
|
||||
- name: Install host Cap'n Proto Go plugin
|
||||
# capnpc-go must run on the GitHub runner, not the matrix target.
|
||||
run: env -u GOOS -u GOARCH -u GOARM -u GOMIPS -u CGO_ENABLED go install capnproto.org/go/capnp/v3/[email protected]
|
||||
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
|
||||
- name: Generate host Cap'n Proto bindings
|
||||
# go generate builds and executes generate.go, so it must use host Go env.
|
||||
run: env -u GOOS -u GOARCH -u GOARM -u GOMIPS -u CGO_ENABLED go generate ./servicecapnp
|
||||
|
||||
- name: Build rz-silhouette-server
|
||||
run: go build -o rz-silhouette-server-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goarm }}
|
||||
|
||||
+6
-1
@@ -1 +1,6 @@
|
||||
rz-silhouette-server
|
||||
__pycache__/
|
||||
.tmp/
|
||||
config.local.yaml
|
||||
state/
|
||||
artifacts/
|
||||
servicecapnp/service.capnp.go
|
||||
@@ -1,27 +1,40 @@
|
||||
# Rizin Silhouette Server
|
||||
|
||||
This server is used to provide signatures using the [rz-silhouette](https://github.com/rizinorg/rz-silhouette) plugin for rizin.
|
||||
This server provides signatures, symbols, and hints to the [rz-silhouette](https://github.com/rizinorg/rz-silhouette) plugin for Rizin.
|
||||
|
||||
## Generating signatures
|
||||
## Usage
|
||||
|
||||
1. Build the https://github.com/rizinorg/rz-silhouette-server and install https://github.com/rizinorg/rz-silhouette
|
||||
2. Run `rz-silhouette-server -c config.yaml`
|
||||
3. Execute `rizin -Qc 'aa ; sil share' /path/to/the/binary.exe`
|
||||
|
||||
Ensure that the `rizin` plugin `rz-silhouette` is configured to point towards the server and that the user can share the info with the server.
|
||||
Ensure that the `rizin` plugin `rz-silhouette` points to the server and that the configured PSK is allowed to upload.
|
||||
|
||||
## Info the server provides
|
||||
## Compilation
|
||||
|
||||
The server will provide 2 types of info
|
||||
Install Go, `capnp`, and `capnpc-go`, then build from a clean checkout:
|
||||
|
||||
1. Hints based on the match on the binary section
|
||||
2. Symbols based on their signatures
|
||||
```sh
|
||||
go generate ./servicecapnp
|
||||
go build
|
||||
```
|
||||
|
||||
## Server behaviour
|
||||
For local installation, place the resulting `rz-silhouette-server` binary somewhere on your `PATH` or run it from the build directory.
|
||||
|
||||
The server stores the info into databases.
|
||||
Some clients (based on the configuration) can be allowed to upload new signatures and hints.
|
||||
For cross-compilation, generate the bindings on the build host first and then run `go build` with the target `GOOS` and `GOARCH`. Do not run `go generate` under the target environment, because the generator itself must execute on the host machine.
|
||||
|
||||
## Protocol
|
||||
|
||||
The server provides a Cap'n Proto exact-match resolve/share protocol.
|
||||
|
||||
## Storage
|
||||
|
||||
The server stores exact-match data in BoltDB. Some clients, based on the configuration, can upload new signatures and hints.
|
||||
|
||||
## Security
|
||||
|
||||
Cap'n Proto does not encrypt traffic. If you expose the raw TCP port, the PSK is still sent in clear text. Set `capnp_require_tls: true` and use the TLS listener for protected deployments.
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available [here](https://github.com/rizinorg/rz-silhouette/tree/main/docs)
|
||||
Documentation is available [here](https://github.com/rizinorg/rz-silhouette/tree/main/docs)
|
||||
@@ -5,10 +5,11 @@ package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
@@ -18,32 +19,32 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
MIN_VERSION = uint32(1)
|
||||
GENERIC_DB = "any"
|
||||
GENERIC_DB = "any"
|
||||
)
|
||||
|
||||
type Resource struct {
|
||||
Arch string `yaml:"arch"`
|
||||
Bits int `yaml:"bits`
|
||||
Bits int `yaml:"bits"`
|
||||
Files []string `yaml:"files"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
MaxQueue int `yaml:"max_queue"`
|
||||
MaxPacket int64 `yaml:"max_packet"`
|
||||
LogLevel string `yaml:"log_level"`
|
||||
RawBind string `yaml:"raw-bind"`
|
||||
TlsBind string `yaml:"tls-bind"`
|
||||
TlsKey string `yaml:"tls-key"`
|
||||
TlsCert string `yaml:"tls-cert"`
|
||||
Message string `yaml:"message"`
|
||||
UploadDir string `yaml:"upload_dir"`
|
||||
Resources []Resource `yaml:"resources"`
|
||||
Authorized map[string]bool `yaml:"authorized"`
|
||||
MaxQueue int `yaml:"max_queue"`
|
||||
MaxPacket int64 `yaml:"max_packet"`
|
||||
LogLevel string `yaml:"log_level"`
|
||||
RawBind string `yaml:"raw-bind"`
|
||||
TlsBind string `yaml:"tls-bind"`
|
||||
TlsKey string `yaml:"tls-key"`
|
||||
TlsCert string `yaml:"tls-cert"`
|
||||
Message string `yaml:"message"`
|
||||
UploadDir string `yaml:"upload_dir"`
|
||||
CapnpRequireTLS bool `yaml:"capnp_require_tls"`
|
||||
Resources []Resource `yaml:"resources"`
|
||||
Authorized map[string]bool `yaml:"authorized"`
|
||||
}
|
||||
|
||||
func readConfig(filename string, config *Config) error {
|
||||
body, err := ioutil.ReadFile(filename)
|
||||
body, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -81,6 +82,36 @@ func readConfig(filename string, config *Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func sharedDBFilename(dir, key string) string {
|
||||
sum := sha256.Sum256([]byte(key))
|
||||
return filepath.Join(dir, fmt.Sprintf("%x.db", sum))
|
||||
}
|
||||
|
||||
func legacySharedDBFilename(dir, key string) string {
|
||||
sum := md5.Sum([]byte(key))
|
||||
return filepath.Join(dir, fmt.Sprintf("%x.db", sum))
|
||||
}
|
||||
|
||||
func resolveSharedDBFilename(dir, key string) (string, error) {
|
||||
current := sharedDBFilename(dir, key)
|
||||
if exists(current) {
|
||||
return current, nil
|
||||
}
|
||||
|
||||
legacy := legacySharedDBFilename(dir, key)
|
||||
if !exists(legacy) {
|
||||
return current, nil
|
||||
}
|
||||
|
||||
if err := os.Rename(legacy, current); err == nil {
|
||||
log.Warn().Msgf("migrated legacy shared db %s -> %s", legacy, current)
|
||||
return current, nil
|
||||
}
|
||||
|
||||
log.Warn().Msgf("using legacy shared db path %s because migration to %s failed", legacy, current)
|
||||
return legacy, nil
|
||||
}
|
||||
|
||||
func (c *Config) GetAuthorized() map[string]bool {
|
||||
if c.UploadDir != "" {
|
||||
return c.Authorized
|
||||
@@ -176,8 +207,10 @@ func (c *Config) LoadResources() (map[string][]*bbolt.DB, map[string]*bbolt.DB)
|
||||
if !canUpload {
|
||||
continue
|
||||
}
|
||||
dbFile := fmt.Sprintf("%x.db", md5.Sum([]byte(key)))
|
||||
dbFile = filepath.Join(c.UploadDir, dbFile)
|
||||
dbFile, err := resolveSharedDBFilename(c.UploadDir, key)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Send()
|
||||
}
|
||||
|
||||
db, err := OpenDatabase(dbFile)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSharedDBFilenameUsesSHA256(t *testing.T) {
|
||||
got := sharedDBFilename("/tmp/share", "demo-psk")
|
||||
want := filepath.Join("/tmp/share", fmt.Sprintf("%x.db", sha256.Sum256([]byte("demo-psk"))))
|
||||
if got != want {
|
||||
t.Fatalf("sharedDBFilename() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacySharedDBFilenameUsesMD5(t *testing.T) {
|
||||
got := legacySharedDBFilename("/tmp/share", "demo-psk")
|
||||
want := filepath.Join("/tmp/share", fmt.Sprintf("%x.db", md5.Sum([]byte("demo-psk"))))
|
||||
if got != want {
|
||||
t.Fatalf("legacySharedDBFilename() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadResourcesMigratesLegacySharedDBFilename(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
psk := "demo-psk"
|
||||
legacyPath := legacySharedDBFilename(dir, psk)
|
||||
db, err := OpenDatabase(legacyPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db.Close()
|
||||
|
||||
cfg := &Config{
|
||||
MaxQueue: 1024,
|
||||
MaxPacket: 1024 * 1024,
|
||||
LogLevel: "warn",
|
||||
UploadDir: dir,
|
||||
Authorized: map[string]bool{psk: true},
|
||||
}
|
||||
|
||||
_, shared := cfg.LoadResources()
|
||||
sharedDB := shared[psk]
|
||||
if sharedDB == nil {
|
||||
t.Fatal("expected shared db to be opened")
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
sharedDB.Close()
|
||||
})
|
||||
|
||||
currentPath := sharedDBFilename(dir, psk)
|
||||
if !exists(currentPath) {
|
||||
t.Fatalf("expected migrated db at %q", currentPath)
|
||||
}
|
||||
if exists(legacyPath) {
|
||||
t.Fatalf("expected legacy db %q to be migrated away", legacyPath)
|
||||
}
|
||||
if _, err := os.Stat(currentPath); err != nil {
|
||||
t.Fatalf("expected migrated db to be readable: %v", err)
|
||||
}
|
||||
}
|
||||
+91
-14
@@ -6,6 +6,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.etcd.io/bbolt"
|
||||
@@ -19,17 +20,21 @@ const (
|
||||
var (
|
||||
DB_BUCKET_SYM = []byte("Symbols")
|
||||
DB_BUCKET_SEC = []byte("Sections")
|
||||
DB_BUCKET_LOC = []byte("Locations")
|
||||
)
|
||||
|
||||
type MetaHints struct {
|
||||
Author string
|
||||
Name string
|
||||
Hints []*Hint
|
||||
Author string `json:"author"`
|
||||
Name string `json:"name"`
|
||||
BinaryID string `json:"binary_id,omitempty"`
|
||||
Hints []*Hint `json:"hints"`
|
||||
}
|
||||
|
||||
type MetaSymbol struct {
|
||||
Author string
|
||||
Symbol *Symbol
|
||||
Author string `json:"author"`
|
||||
BinaryID string `json:"binary_id,omitempty"`
|
||||
Symbol *Symbol `json:"symbol"`
|
||||
Size uint32 `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
func SignatureToKey(s *Signature) []byte {
|
||||
@@ -42,6 +47,11 @@ func SignatureToKey(s *Signature) []byte {
|
||||
return append([]byte(prefix), s.Digest...)
|
||||
}
|
||||
|
||||
func FunctionLocationToKey(binaryID, sectionName string, sectionPaddr, sectionOffset uint64) []byte {
|
||||
sectionName = strings.TrimSpace(sectionName)
|
||||
return []byte(fmt.Sprintf("%s|%s|%016x|%016x", binaryID, sectionName, sectionPaddr, sectionOffset))
|
||||
}
|
||||
|
||||
func SectionHashToKey(sh *SectionHash, binType, binOs string) []byte {
|
||||
btype := sanitizeWord(binType, "any")
|
||||
bos := sanitizeWord(binOs, "any")
|
||||
@@ -73,6 +83,14 @@ func getValue(db *bbolt.DB, name, key []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func DbGetHints(db *bbolt.DB, key []byte) ([]*Hint, error) {
|
||||
meta, err := DbGetHintsMeta(db, key)
|
||||
if meta == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return meta.Hints, nil
|
||||
}
|
||||
|
||||
func DbGetHintsMeta(db *bbolt.DB, key []byte) (*MetaHints, error) {
|
||||
value, err := getValue(db, DB_BUCKET_SEC, key)
|
||||
if value == nil {
|
||||
return nil, err
|
||||
@@ -80,7 +98,7 @@ func DbGetHints(db *bbolt.DB, key []byte) ([]*Hint, error) {
|
||||
|
||||
ms := MetaHints{}
|
||||
err = json.Unmarshal(value, &ms)
|
||||
return ms.Hints, err
|
||||
return &ms, err
|
||||
}
|
||||
|
||||
func DbHasHints(db *bbolt.DB, key []byte) (bool, error) {
|
||||
@@ -88,11 +106,12 @@ func DbHasHints(db *bbolt.DB, key []byte) (bool, error) {
|
||||
return value != nil, err
|
||||
}
|
||||
|
||||
func DbSetHints(db *bbolt.DB, key []byte, author, name string, hints []*Hint) error {
|
||||
func DbSetHintsMeta(db *bbolt.DB, key []byte, author, name, binaryID string, hints []*Hint) error {
|
||||
ms := MetaHints{
|
||||
Author: author,
|
||||
Name: name,
|
||||
Hints: hints,
|
||||
Author: author,
|
||||
Name: name,
|
||||
BinaryID: binaryID,
|
||||
Hints: hints,
|
||||
}
|
||||
|
||||
value, err := json.Marshal(&ms)
|
||||
@@ -103,7 +122,19 @@ func DbSetHints(db *bbolt.DB, key []byte, author, name string, hints []*Hint) er
|
||||
return setValue(db, DB_BUCKET_SEC, key, value)
|
||||
}
|
||||
|
||||
func DbSetHints(db *bbolt.DB, key []byte, author, name string, hints []*Hint) error {
|
||||
return DbSetHintsMeta(db, key, author, name, "", hints)
|
||||
}
|
||||
|
||||
func DbGetSymbol(db *bbolt.DB, key []byte) (*Symbol, error) {
|
||||
meta, err := DbGetSymbolMeta(db, key)
|
||||
if meta == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return meta.Symbol, nil
|
||||
}
|
||||
|
||||
func DbGetSymbolMeta(db *bbolt.DB, key []byte) (*MetaSymbol, error) {
|
||||
value, err := getValue(db, DB_BUCKET_SYM, key)
|
||||
if value == nil {
|
||||
return nil, err
|
||||
@@ -111,7 +142,18 @@ func DbGetSymbol(db *bbolt.DB, key []byte) (*Symbol, error) {
|
||||
|
||||
ms := MetaSymbol{}
|
||||
err = json.Unmarshal(value, &ms)
|
||||
return ms.Symbol, err
|
||||
return &ms, err
|
||||
}
|
||||
|
||||
func DbGetLocationSymbolMeta(db *bbolt.DB, key []byte) (*MetaSymbol, error) {
|
||||
value, err := getValue(db, DB_BUCKET_LOC, key)
|
||||
if value == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ms := MetaSymbol{}
|
||||
err = json.Unmarshal(value, &ms)
|
||||
return &ms, err
|
||||
}
|
||||
|
||||
func DbHasSymbol(db *bbolt.DB, key []byte) (bool, error) {
|
||||
@@ -119,14 +161,21 @@ func DbHasSymbol(db *bbolt.DB, key []byte) (bool, error) {
|
||||
return value != nil, err
|
||||
}
|
||||
|
||||
func DbSetSymbol(db *bbolt.DB, key []byte, author string, sym *Symbol) error {
|
||||
func DbHasLocationSymbol(db *bbolt.DB, key []byte) (bool, error) {
|
||||
value, err := getValue(db, DB_BUCKET_LOC, key)
|
||||
return value != nil, err
|
||||
}
|
||||
|
||||
func DbSetSymbolMeta(db *bbolt.DB, key []byte, author, binaryID string, sym *Symbol, size uint32) error {
|
||||
if sym == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ms := MetaSymbol{
|
||||
Author: author,
|
||||
Symbol: sym,
|
||||
Author: author,
|
||||
BinaryID: binaryID,
|
||||
Symbol: sym,
|
||||
Size: size,
|
||||
}
|
||||
|
||||
value, err := json.Marshal(&ms)
|
||||
@@ -137,6 +186,30 @@ func DbSetSymbol(db *bbolt.DB, key []byte, author string, sym *Symbol) error {
|
||||
return setValue(db, DB_BUCKET_SYM, key, value)
|
||||
}
|
||||
|
||||
func DbSetLocationSymbolMeta(db *bbolt.DB, key []byte, author, binaryID string, sym *Symbol, size uint32) error {
|
||||
if sym == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ms := MetaSymbol{
|
||||
Author: author,
|
||||
BinaryID: binaryID,
|
||||
Symbol: sym,
|
||||
Size: size,
|
||||
}
|
||||
|
||||
value, err := json.Marshal(&ms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return setValue(db, DB_BUCKET_LOC, key, value)
|
||||
}
|
||||
|
||||
func DbSetSymbol(db *bbolt.DB, key []byte, author string, sym *Symbol) error {
|
||||
return DbSetSymbolMeta(db, key, author, "", sym, 0)
|
||||
}
|
||||
|
||||
func initDatabase(tx *bbolt.Tx) error {
|
||||
_, err := tx.CreateBucketIfNotExists(DB_BUCKET_SYM)
|
||||
if err != nil {
|
||||
@@ -146,6 +219,10 @@ func initDatabase(tx *bbolt.Tx) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.CreateBucketIfNotExists(DB_BUCKET_LOC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
func TestOpenDatabaseUpgradesLegacyBuckets(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "legacy.db")
|
||||
|
||||
legacyDB, err := bbolt.Open(path, DB_PERM, &bbolt.Options{Timeout: 2 * time.Second})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = legacyDB.Update(func(tx *bbolt.Tx) error {
|
||||
if _, err := tx.CreateBucketIfNotExists(DB_BUCKET_SYM); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tx.CreateBucketIfNotExists(DB_BUCKET_SEC); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
legacyDB.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db, err := OpenDatabase(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = db.View(func(tx *bbolt.Tx) error {
|
||||
if tx.Bucket(DB_BUCKET_SYM) == nil {
|
||||
t.Fatal("Symbols bucket missing")
|
||||
}
|
||||
if tx.Bucket(DB_BUCKET_SEC) == nil {
|
||||
t.Fatal("Sections bucket missing")
|
||||
}
|
||||
if tx.Bucket(DB_BUCKET_LOC) == nil {
|
||||
t.Fatal("Locations bucket missing")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,10 @@ resources:
|
||||
# path to the upload database for those clients that are authorized.
|
||||
upload_dir: /path/to/shared
|
||||
|
||||
# Cap'n Proto only changes serialization. Keep this enabled for real deployments
|
||||
# unless the raw TCP listener is strictly local and trusted.
|
||||
capnp_require_tls: true
|
||||
|
||||
# Map of 'psk: bool' of authorized clients which can and cannot upload
|
||||
authorized:
|
||||
39b1212a-944e-4b4f-83a7-33a3be33e314: true
|
||||
@@ -3,14 +3,16 @@ module rz-silhouette-server
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
capnproto.org/go/capnp/v3 v3.1.0-alpha.2
|
||||
github.com/rs/zerolog v1.29.0
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
google.golang.org/protobuf v1.30.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
)
|
||||
@@ -1,30 +1,31 @@
|
||||
capnproto.org/go/capnp/v3 v3.1.0-alpha.2 h1:93ISpqHf2/3WQlfrBP0tT8Dg/RQc3uq6DV36vN0ePWk=
|
||||
capnproto.org/go/capnp/v3 v3.1.0-alpha.2/go.mod h1:2vT5D2dtG8sJGEoEKU17e+j7shdaYp1Myl8X03B3hmc=
|
||||
github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 h1:d5EKgQfRQvO97jnISfR89AiCCCJMwMFoSxUiU0OGCRU=
|
||||
github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381/go.mod h1:OU76gHeRo8xrzGJU3F3I1CqX1ekM8dfJw0+wPeMwnp0=
|
||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
|
||||
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/tinylib/msgp v1.1.9 h1:SHf3yoO2sGA0veCJeCBYLHuttAVFHGm2RHgNodW7wQU=
|
||||
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
+52
-170
@@ -1,243 +1,125 @@
|
||||
// SPDX-FileCopyrightText: 2023 RizinOrg <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2023 deroad <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
HEADER_SIZE = int64(4)
|
||||
HEADER_SIZE = int64(4)
|
||||
PROTOCOL_MAGIC = "SILC"
|
||||
)
|
||||
|
||||
type requestLogInfo struct {
|
||||
psk string
|
||||
route string
|
||||
version uint32
|
||||
}
|
||||
|
||||
func readFromConn(conn net.Conn, expected int64) ([]byte, bool) {
|
||||
conn.SetReadDeadline(time.Now().Add(TIMEOUT_CONN))
|
||||
var buffer bytes.Buffer
|
||||
writer := io.Writer(&buffer)
|
||||
if expected < 0 || expected > MAX_BODY_LEN {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Msgf("Invalid requested read size %d", expected)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
_, err := io.CopyN(writer, conn, expected)
|
||||
buffer := make([]byte, expected)
|
||||
_, err := io.ReadFull(conn, buffer)
|
||||
if err == io.EOF {
|
||||
return nil, false
|
||||
} else if err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Send()
|
||||
return nil, false
|
||||
}
|
||||
|
||||
b := buffer.Bytes()
|
||||
if len(b) != int(expected) {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Msgf("Expected %d but received only %d", expected, len(b))
|
||||
return nil, false
|
||||
}
|
||||
return b, true
|
||||
return buffer, true
|
||||
}
|
||||
|
||||
func readIncomingRequest(conn net.Conn) *Request {
|
||||
func readIncomingPacket(conn net.Conn) ([]byte, bool) {
|
||||
header, ok := readFromConn(conn, HEADER_SIZE)
|
||||
if !ok {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Msg("Failed to receive packet size.")
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
|
||||
packetSize := int64(binary.BigEndian.Uint32(header))
|
||||
if packetSize > MAX_BODY_LEN {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Msgf("Expected small body size (%d) but received %d", MAX_BODY_LEN, packetSize)
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
|
||||
packet, ok := readFromConn(conn, packetSize)
|
||||
if !ok {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Msg("Failed to receive packet")
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
|
||||
req := &Request{}
|
||||
if err := proto.Unmarshal(packet, req); err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Msg("Failed to decode request")
|
||||
return nil
|
||||
if len(packet) < len(PROTOCOL_MAGIC) || string(packet[:len(PROTOCOL_MAGIC)]) != PROTOCOL_MAGIC {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Msg("Failed to receive capnp packet magic")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return req
|
||||
return packet[len(PROTOCOL_MAGIC):], true
|
||||
}
|
||||
|
||||
func writeToConn(conn net.Conn, buffer []byte) bool {
|
||||
var b = bytes.NewBuffer(buffer)
|
||||
reader := io.Reader(b)
|
||||
|
||||
written, err := io.Copy(conn, reader)
|
||||
if err == io.EOF {
|
||||
return false
|
||||
} else if err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Send()
|
||||
return false
|
||||
} else if int(written) != len(buffer) {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Msg("Failed to send all the bytes")
|
||||
return false
|
||||
offset := 0
|
||||
for offset < len(buffer) {
|
||||
written, err := conn.Write(buffer[offset:])
|
||||
if err == io.EOF {
|
||||
return false
|
||||
} else if err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Send()
|
||||
return false
|
||||
}
|
||||
offset += written
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func writeOutgoingResponse(conn net.Conn, body proto.Message, status Status) bool {
|
||||
var message []byte = nil
|
||||
var err error = nil
|
||||
|
||||
if body != nil {
|
||||
message, err = proto.Marshal(body)
|
||||
if err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Msg("Failed to encode body")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
resp := Response{
|
||||
Status: status,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
packet, err := proto.Marshal(&resp)
|
||||
if err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Msg("Failed to encode response")
|
||||
return false
|
||||
}
|
||||
|
||||
func writePacket(conn net.Conn, body []byte) bool {
|
||||
body = append([]byte(PROTOCOL_MAGIC), body...)
|
||||
header := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(header[:], uint32(len(packet)))
|
||||
if writeToConn(conn, header) && writeToConn(conn, packet) {
|
||||
binary.BigEndian.PutUint32(header[:], uint32(len(body)))
|
||||
if writeToConn(conn, header) && writeToConn(conn, body) {
|
||||
return true
|
||||
}
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Msg("Failed to send response packet")
|
||||
return false
|
||||
}
|
||||
|
||||
func handlePingRequest(server *Server, conn net.Conn, request *Request) {
|
||||
message := Message{
|
||||
Text: server.motd,
|
||||
}
|
||||
writeOutgoingResponse(conn, &message, Status_MESSAGE)
|
||||
func isTLSConn(conn net.Conn) bool {
|
||||
_, ok := conn.(*tls.Conn)
|
||||
return ok
|
||||
}
|
||||
|
||||
func handleBinaryRequest(server *Server, conn net.Conn, request *Request) {
|
||||
var binary = Binary{}
|
||||
if err := proto.Unmarshal(request.Message, &binary); err != nil {
|
||||
log.Info().
|
||||
Stringer("ip", conn.RemoteAddr()).
|
||||
Str("psk", request.Psk).
|
||||
Stringer("route", request.Route).
|
||||
Err(err).
|
||||
Send()
|
||||
return
|
||||
}
|
||||
|
||||
var matches = MatchHints{}
|
||||
for _, section := range binary.Sections {
|
||||
hints := server.GetHints(request.Psk, &binary, section)
|
||||
if hints == nil {
|
||||
continue
|
||||
}
|
||||
for _, hint := range hints {
|
||||
hint.Offset += section.Paddr
|
||||
}
|
||||
if matches.Hints == nil {
|
||||
matches.Hints = hints
|
||||
} else {
|
||||
matches.Hints = append(matches.Hints, hints...)
|
||||
}
|
||||
}
|
||||
|
||||
writeOutgoingResponse(conn, &matches, Status_HINTS)
|
||||
}
|
||||
|
||||
func handleSignatureRequest(server *Server, conn net.Conn, request *Request) {
|
||||
var signature = Signature{}
|
||||
if err := proto.Unmarshal(request.Message, &signature); err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).
|
||||
Str("psk", request.Psk).
|
||||
Stringer("route", request.Route).
|
||||
Err(err).
|
||||
Send()
|
||||
return
|
||||
}
|
||||
|
||||
message := server.GetSymbol(request.Psk, &signature)
|
||||
writeOutgoingResponse(conn, message, Status_SYMBOL)
|
||||
}
|
||||
|
||||
func handleShareBinRequest(server *Server, conn net.Conn, request *Request) {
|
||||
if !server.CanShare(request) {
|
||||
writeOutgoingResponse(conn, nil, Status_CLIENT_NOT_AUTHORIZED)
|
||||
return
|
||||
}
|
||||
|
||||
var bin = ShareBin{}
|
||||
if err := proto.Unmarshal(request.Message, &bin); err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).
|
||||
Str("psk", request.Psk).
|
||||
Stringer("route", request.Route).
|
||||
Err(err).
|
||||
Send()
|
||||
return
|
||||
}
|
||||
|
||||
if bin.Sections != nil {
|
||||
for _, section := range bin.Sections {
|
||||
server.SetHints(request.Psk, &bin, section)
|
||||
}
|
||||
}
|
||||
|
||||
if bin.Symbols != nil {
|
||||
for _, share := range bin.Symbols {
|
||||
server.SetSymbol(request.Psk, share.Signature, share.Symbol)
|
||||
}
|
||||
}
|
||||
|
||||
writeOutgoingResponse(conn, nil, Status_SHARE_WAS_SUCCESSFUL)
|
||||
}
|
||||
|
||||
func logRequest(conn net.Conn, request *Request, start time.Time) {
|
||||
func logRequest(conn net.Conn, info requestLogInfo, start time.Time) {
|
||||
end := time.Now()
|
||||
log.Warn().
|
||||
Stringer("ip", conn.RemoteAddr()).
|
||||
Str("psk", request.Psk).
|
||||
Stringer("route", request.Route).
|
||||
Str("codec", "capnp").
|
||||
Str("psk", info.psk).
|
||||
Str("route", info.route).
|
||||
Uint32("version", info.version).
|
||||
TimeDiff("duration", end, start).
|
||||
Send()
|
||||
}
|
||||
|
||||
func handleConnection(server *Server, conn net.Conn) {
|
||||
var start = time.Now()
|
||||
var request *Request = nil
|
||||
|
||||
start := time.Now()
|
||||
defer conn.Close()
|
||||
|
||||
request = readIncomingRequest(conn)
|
||||
if request == nil {
|
||||
packet, ok := readIncomingPacket(conn)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if !server.IsAuthorized(request) {
|
||||
writeOutgoingResponse(conn, nil, Status_CLIENT_BAD_PRE_SHARED_KEY)
|
||||
logRequest(conn, request, start)
|
||||
|
||||
return
|
||||
info, handled := handleCapnpConnection(server, conn, packet)
|
||||
if handled {
|
||||
logRequest(conn, info, start)
|
||||
}
|
||||
|
||||
switch request.Route {
|
||||
case Route_PING:
|
||||
handlePingRequest(server, conn, request)
|
||||
case Route_BINARY:
|
||||
handleBinaryRequest(server, conn, request)
|
||||
case Route_SIGNATURE:
|
||||
handleSignatureRequest(server, conn, request)
|
||||
case Route_SHARE_BIN:
|
||||
handleShareBinRequest(server, conn, request)
|
||||
default:
|
||||
}
|
||||
logRequest(conn, request, start)
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
capnp "capnproto.org/go/capnp/v3"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"rz-silhouette-server/servicecapnp"
|
||||
)
|
||||
|
||||
type capnpRequest struct {
|
||||
Psk string
|
||||
Version uint32
|
||||
Route servicecapnp.SilRoute
|
||||
Program ProgramBundle
|
||||
}
|
||||
|
||||
func decodeCapnpRequest(packet []byte) (capnpRequest, error) {
|
||||
msg, err := capnp.UnmarshalPacked(packet)
|
||||
if err != nil {
|
||||
return capnpRequest{}, err
|
||||
}
|
||||
|
||||
root, err := servicecapnp.ReadRootSilRequest(msg)
|
||||
if err != nil {
|
||||
return capnpRequest{}, err
|
||||
}
|
||||
|
||||
psk, err := root.Psk()
|
||||
if err != nil {
|
||||
return capnpRequest{}, err
|
||||
}
|
||||
|
||||
req := capnpRequest{
|
||||
Psk: psk,
|
||||
Version: root.Version(),
|
||||
Route: root.Route(),
|
||||
}
|
||||
|
||||
switch root.Which() {
|
||||
case servicecapnp.SilRequest_Which_ping:
|
||||
if req.Route != servicecapnp.SilRoute_ping {
|
||||
return capnpRequest{}, fmt.Errorf("capnp route mismatch: %s/%s", req.Route, root.Which())
|
||||
}
|
||||
case servicecapnp.SilRequest_Which_resolveProgram:
|
||||
if req.Route != servicecapnp.SilRoute_resolveProgram {
|
||||
return capnpRequest{}, fmt.Errorf("capnp route mismatch: %s/%s", req.Route, root.Which())
|
||||
}
|
||||
resolveReq, err := root.ResolveProgram()
|
||||
if err != nil {
|
||||
return capnpRequest{}, err
|
||||
}
|
||||
req.Program, err = decodeProgramBundle(resolveReq.Program)
|
||||
if err != nil {
|
||||
return capnpRequest{}, err
|
||||
}
|
||||
case servicecapnp.SilRequest_Which_shareProgram:
|
||||
if req.Route != servicecapnp.SilRoute_shareProgram {
|
||||
return capnpRequest{}, fmt.Errorf("capnp route mismatch: %s/%s", req.Route, root.Which())
|
||||
}
|
||||
shareReq, err := root.ShareProgram()
|
||||
if err != nil {
|
||||
return capnpRequest{}, err
|
||||
}
|
||||
req.Program, err = decodeProgramBundle(shareReq.Program)
|
||||
if err != nil {
|
||||
return capnpRequest{}, err
|
||||
}
|
||||
default:
|
||||
return capnpRequest{}, fmt.Errorf("unsupported capnp union %s", root.Which())
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeProgramBundle(get func() (servicecapnp.SilProgramBundle, error)) (ProgramBundle, error) {
|
||||
program, err := get()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
|
||||
binaryType, err := program.BinaryType()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
binaryOS, err := program.Os()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
arch, err := program.Arch()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
binaryID, err := program.BinaryId()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
|
||||
out := ProgramBundle{
|
||||
BinaryType: binaryType,
|
||||
OS: binaryOS,
|
||||
Arch: arch,
|
||||
Bits: program.Bits(),
|
||||
BinaryID: binaryID,
|
||||
}
|
||||
|
||||
sections, err := program.Sections()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
for i := 0; i < sections.Len(); i++ {
|
||||
section := sections.At(i)
|
||||
digest, err := section.Digest()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
name, err := section.Name()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
out.Sections = append(out.Sections, SectionRecord{
|
||||
Name: name,
|
||||
Size: section.Size(),
|
||||
Paddr: section.Paddr(),
|
||||
Digest: append([]byte(nil), digest...),
|
||||
})
|
||||
}
|
||||
|
||||
functions, err := program.Functions()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
for i := 0; i < functions.Len(); i++ {
|
||||
function := functions.At(i)
|
||||
digest, err := function.Digest()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
arch, err := function.Arch()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
sectionName, err := function.SectionName()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
name, err := function.Name()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
signature, err := function.Signature()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
callconv, err := function.Callconv()
|
||||
if err != nil {
|
||||
return ProgramBundle{}, err
|
||||
}
|
||||
out.Functions = append(out.Functions, FunctionRecord{
|
||||
Addr: function.Addr(),
|
||||
Size: function.Size(),
|
||||
Bits: function.Bits(),
|
||||
Arch: arch,
|
||||
Length: function.Length(),
|
||||
Digest: append([]byte(nil), digest...),
|
||||
SectionName: sectionName,
|
||||
SectionPaddr: function.SectionPaddr(),
|
||||
SectionOffset: function.SectionOffset(),
|
||||
Name: name,
|
||||
Signature: signature,
|
||||
Callconv: callconv,
|
||||
})
|
||||
}
|
||||
|
||||
return normalizeProgramBundle(out), nil
|
||||
}
|
||||
|
||||
func writeCapnpMessageResponse(conn net.Conn, status servicecapnp.SilStatus, text string) bool {
|
||||
msg, seg := capnp.NewSingleSegmentMessage(nil)
|
||||
root, err := servicecapnp.NewRootSilResponse(seg)
|
||||
if err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Msg("Failed to allocate capnp response")
|
||||
return false
|
||||
}
|
||||
root.SetStatus(status)
|
||||
message, err := root.NewMessage_()
|
||||
if err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Msg("Failed to allocate capnp message body")
|
||||
return false
|
||||
}
|
||||
if err := message.SetText(text); err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Msg("Failed to set capnp response text")
|
||||
return false
|
||||
}
|
||||
packet, err := msg.MarshalPacked()
|
||||
if err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Msg("Failed to encode capnp response")
|
||||
return false
|
||||
}
|
||||
return writePacket(conn, packet)
|
||||
}
|
||||
|
||||
func writeCapnpServerInfo(conn net.Conn, tlsRequired bool) bool {
|
||||
msg, seg := capnp.NewSingleSegmentMessage(nil)
|
||||
root, err := servicecapnp.NewRootSilResponse(seg)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
root.SetStatus(servicecapnp.SilStatus_serverInfo)
|
||||
serverInfo, err := root.NewServerInfo()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
codecs, err := serverInfo.NewSupportedCodecs(1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
codecs.Set(0, servicecapnp.SilCodec_capnp)
|
||||
serverInfo.SetVersion(PROTOCOL_VERSION)
|
||||
serverInfo.SetTlsRequired(tlsRequired)
|
||||
packet, err := msg.MarshalPacked()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return writePacket(conn, packet)
|
||||
}
|
||||
|
||||
func writeCapnpResolveResult(conn net.Conn, result ResolveProgramResult) bool {
|
||||
msg, seg := capnp.NewSingleSegmentMessage(nil)
|
||||
root, err := servicecapnp.NewRootSilResponse(seg)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
root.SetStatus(servicecapnp.SilStatus_resolveResult)
|
||||
resolveResult, err := root.NewResolveResult()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if err := populateCapnpResolveResult(resolveResult, result); err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Msg("Failed to encode resolve result")
|
||||
return false
|
||||
}
|
||||
packet, err := msg.MarshalPacked()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return writePacket(conn, packet)
|
||||
}
|
||||
|
||||
func populateCapnpResolveResult(dst servicecapnp.SilResolveResult, result ResolveProgramResult) error {
|
||||
hints, err := dst.NewHints(int32(len(result.Hints)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, hint := range result.Hints {
|
||||
entry := hints.At(i)
|
||||
entry.SetBits(hint.Bits)
|
||||
entry.SetOffset(hint.Offset)
|
||||
}
|
||||
|
||||
symbols, err := dst.NewSymbols(int32(len(result.Symbols)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, symbol := range result.Symbols {
|
||||
entry := symbols.At(i)
|
||||
entry.SetAddr(symbol.Addr)
|
||||
entry.SetExact(symbol.Exact)
|
||||
entry.SetOffset(symbol.Offset)
|
||||
entry.SetSize(symbol.Size)
|
||||
if err := entry.SetMatchedBinaryId(symbol.MatchedBinaryID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := entry.SetMatchedBy(symbol.MatchedBy); err != nil {
|
||||
return err
|
||||
}
|
||||
sym, err := entry.NewSymbol()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sym.SetName(symbol.Symbol.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sym.SetSignature(symbol.Symbol.Signature); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sym.SetCallconv(symbol.Symbol.Callconv); err != nil {
|
||||
return err
|
||||
}
|
||||
sym.SetBits(symbol.Symbol.Bits)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeCapnpShareResult(conn net.Conn, result ShareProgramResult) bool {
|
||||
msg, seg := capnp.NewSingleSegmentMessage(nil)
|
||||
root, err := servicecapnp.NewRootSilResponse(seg)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
root.SetStatus(servicecapnp.SilStatus_shareResult)
|
||||
shareResult, err := root.NewShareResult()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if err := shareResult.SetBinaryId(result.BinaryID); err != nil {
|
||||
return false
|
||||
}
|
||||
packet, err := msg.MarshalPacked()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return writePacket(conn, packet)
|
||||
}
|
||||
|
||||
func handleCapnpConnection(server *Server, conn net.Conn, packet []byte) (requestLogInfo, bool) {
|
||||
request, err := decodeCapnpRequest(packet)
|
||||
if err != nil {
|
||||
log.Info().Stringer("ip", conn.RemoteAddr()).Err(err).Msg("Failed to decode capnp request")
|
||||
return requestLogInfo{}, false
|
||||
}
|
||||
|
||||
info := requestLogInfo{
|
||||
psk: request.Psk,
|
||||
route: request.Route.String(),
|
||||
version: request.Version,
|
||||
}
|
||||
|
||||
_, exists := server.auths[request.Psk]
|
||||
if request.Psk == "" || !exists {
|
||||
writeCapnpMessageResponse(conn, servicecapnp.SilStatus_clientBadPreSharedKey, "client pre-shared key was rejected")
|
||||
return info, true
|
||||
}
|
||||
if request.Version != PROTOCOL_VERSION {
|
||||
writeCapnpMessageResponse(conn, servicecapnp.SilStatus_versionMismatch, fmt.Sprintf("client/server capnp protocol mismatch: got %d want %d", request.Version, PROTOCOL_VERSION))
|
||||
return info, true
|
||||
}
|
||||
if server.capnpRequireTLS && !isTLSConn(conn) {
|
||||
writeCapnpMessageResponse(conn, servicecapnp.SilStatus_clientNotAuthorized, "capnp requires TLS")
|
||||
return info, true
|
||||
}
|
||||
|
||||
switch request.Route {
|
||||
case servicecapnp.SilRoute_ping:
|
||||
writeCapnpServerInfo(conn, server.capnpRequireTLS)
|
||||
case servicecapnp.SilRoute_resolveProgram:
|
||||
result := server.ResolveProgram(request.Psk, request.Program)
|
||||
writeCapnpResolveResult(conn, result)
|
||||
case servicecapnp.SilRoute_shareProgram:
|
||||
if !server.auths[request.Psk] {
|
||||
writeCapnpMessageResponse(conn, servicecapnp.SilStatus_clientNotAuthorized, "client is not allowed to share")
|
||||
return info, true
|
||||
}
|
||||
result := server.ShareProgram(request.Psk, request.Program)
|
||||
writeCapnpShareResult(conn, result)
|
||||
default:
|
||||
writeCapnpMessageResponse(conn, servicecapnp.SilStatus_internalError, "unsupported capnp route")
|
||||
}
|
||||
return info, true
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
capnp "capnproto.org/go/capnp/v3"
|
||||
|
||||
"rz-silhouette-server/servicecapnp"
|
||||
)
|
||||
|
||||
func buildCapnpPingPacket(packed bool) ([]byte, error) {
|
||||
msg, seg := capnp.NewSingleSegmentMessage(nil)
|
||||
root, err := servicecapnp.NewRootSilRequest(seg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := root.SetPsk("demo-psk"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root.SetVersion(PROTOCOL_VERSION)
|
||||
root.SetRoute(servicecapnp.SilRoute_ping)
|
||||
if _, err := root.NewPing(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if packed {
|
||||
return msg.MarshalPacked()
|
||||
}
|
||||
return msg.Marshal()
|
||||
}
|
||||
|
||||
func TestDecodeCapnpRequestPing(t *testing.T) {
|
||||
packet, err := buildCapnpPingPacket(true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req, err := decodeCapnpRequest(packet)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if req.Psk != "demo-psk" {
|
||||
t.Fatalf("unexpected psk: %q", req.Psk)
|
||||
}
|
||||
if req.Version != PROTOCOL_VERSION {
|
||||
t.Fatalf("unexpected version: %d", req.Version)
|
||||
}
|
||||
if req.Route != servicecapnp.SilRoute_ping {
|
||||
t.Fatalf("unexpected route: %s", req.Route)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeCapnpRequestPingUnpacked(t *testing.T) {
|
||||
packet, err := buildCapnpPingPacket(false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg, err := capnp.Unmarshal(packet)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root, err := servicecapnp.ReadRootSilRequest(msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
psk, err := root.Psk()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if psk != "demo-psk" {
|
||||
t.Fatalf("unexpected psk: %q", psk)
|
||||
}
|
||||
if root.Version() != PROTOCOL_VERSION {
|
||||
t.Fatalf("unexpected version: %d", root.Version())
|
||||
}
|
||||
if root.Route() != servicecapnp.SilRoute_ping {
|
||||
t.Fatalf("unexpected route: %s", root.Route())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapnpPingGoldenFixture(t *testing.T) {
|
||||
packet, err := buildCapnpPingPacket(true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, err := os.ReadFile(filepath.Join("testdata", "capnp_ping_request.hex"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := hex.EncodeToString(packet)
|
||||
want := strings.TrimSpace(string(body))
|
||||
if got != want {
|
||||
t.Fatalf("capnp ping fixture drifted\n got: %s\nwant: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPingCodecCapnpPacked(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
packet, err := buildCapnpPingPacket(true)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if _, err := decodeCapnpRequest(packet); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPingCodecCapnpUnpacked(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
packet, err := buildCapnpPingPacket(false)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
msg, err := capnp.Unmarshal(packet)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if _, err := servicecapnp.ReadRootSilRequest(msg); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func functionSignature(bundle ProgramBundle, fn FunctionRecord) *Signature {
|
||||
if len(fn.Digest) < 1 {
|
||||
return nil
|
||||
}
|
||||
arch := fn.Arch
|
||||
if arch == "" {
|
||||
arch = bundle.Arch
|
||||
}
|
||||
length := fn.Length
|
||||
if length < 1 {
|
||||
length = fn.Size
|
||||
}
|
||||
bits := fn.Bits
|
||||
if bits < 1 {
|
||||
bits = bundle.Bits
|
||||
}
|
||||
return &Signature{
|
||||
Arch: arch,
|
||||
Bits: bits,
|
||||
Length: length,
|
||||
Digest: append([]byte(nil), fn.Digest...),
|
||||
}
|
||||
}
|
||||
|
||||
func functionSymbol(fn FunctionRecord, bundleBits uint32) *Symbol {
|
||||
if fn.Name == "" {
|
||||
return nil
|
||||
}
|
||||
bits := fn.Bits
|
||||
if bits < 1 {
|
||||
bits = bundleBits
|
||||
}
|
||||
return &Symbol{
|
||||
Name: fn.Name,
|
||||
Signature: fn.Signature,
|
||||
Callconv: fn.Callconv,
|
||||
Bits: bits,
|
||||
}
|
||||
}
|
||||
|
||||
func sectionLookupKey(name string, paddr uint64) string {
|
||||
return fmt.Sprintf("%s|%016x", name, paddr)
|
||||
}
|
||||
|
||||
func sectionOffsetKey(name string, paddr, offset uint64) string {
|
||||
return fmt.Sprintf("%s|%016x|%016x", name, paddr, offset)
|
||||
}
|
||||
|
||||
func hintedSymbolSize(hints []*Hint, currentOffset uint64, fallback uint32) uint32 {
|
||||
const maxMaterialize = 256
|
||||
best := uint64(0)
|
||||
for _, hint := range hints {
|
||||
if hint == nil || hint.Offset <= currentOffset {
|
||||
continue
|
||||
}
|
||||
span := hint.Offset - currentOffset
|
||||
if span == 0 {
|
||||
continue
|
||||
}
|
||||
if best == 0 || span < best {
|
||||
best = span
|
||||
}
|
||||
}
|
||||
|
||||
if fallback > 0 && (best == 0 || uint64(fallback) < best) {
|
||||
best = uint64(fallback)
|
||||
}
|
||||
if best == 0 {
|
||||
best = 16
|
||||
}
|
||||
if best > maxMaterialize {
|
||||
best = maxMaterialize
|
||||
}
|
||||
return uint32(best)
|
||||
}
|
||||
|
||||
func buildShareSections(bundle ProgramBundle) []*ShareSection {
|
||||
sectionsByKey := make(map[string]SectionRecord, len(bundle.Sections))
|
||||
sectionsByAddr := make(map[uint64]SectionRecord, len(bundle.Sections))
|
||||
for _, section := range bundle.Sections {
|
||||
sectionsByKey[sectionLookupKey(section.Name, section.Paddr)] = section
|
||||
sectionsByAddr[section.Paddr] = section
|
||||
}
|
||||
|
||||
shared := map[string]*ShareSection{}
|
||||
for _, fn := range bundle.Functions {
|
||||
section, ok := sectionsByKey[sectionLookupKey(fn.SectionName, fn.SectionPaddr)]
|
||||
if !ok {
|
||||
section, ok = sectionsByAddr[fn.SectionPaddr]
|
||||
}
|
||||
if !ok || len(section.Digest) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := sectionLookupKey(section.Name, section.Paddr)
|
||||
entry, ok := shared[key]
|
||||
if !ok {
|
||||
entry = &ShareSection{
|
||||
Name: section.Name,
|
||||
Section: &SectionHash{
|
||||
Size: section.Size,
|
||||
Paddr: section.Paddr,
|
||||
Digest: append([]byte(nil), section.Digest...),
|
||||
},
|
||||
}
|
||||
shared[key] = entry
|
||||
}
|
||||
|
||||
bits := fn.Bits
|
||||
if bits < 1 {
|
||||
bits = bundle.Bits
|
||||
}
|
||||
entry.Hints = append(entry.Hints, &Hint{
|
||||
Bits: bits,
|
||||
Offset: fn.SectionOffset,
|
||||
})
|
||||
}
|
||||
|
||||
out := make([]*ShareSection, 0, len(shared))
|
||||
for _, section := range shared {
|
||||
out = append(out, section)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *Server) ResolveProgram(psk string, bundle ProgramBundle) ResolveProgramResult {
|
||||
bundle = normalizeProgramBundle(bundle)
|
||||
result := ResolveProgramResult{}
|
||||
|
||||
sectionMetas := make(map[string]*MetaHints, len(bundle.Sections))
|
||||
for _, section := range bundle.Sections {
|
||||
meta := s.GetHintsMeta(psk, bundle.BinaryType, bundle.OS, &SectionHash{
|
||||
Size: section.Size,
|
||||
Paddr: section.Paddr,
|
||||
Digest: section.Digest,
|
||||
}, nil)
|
||||
if meta == nil {
|
||||
continue
|
||||
}
|
||||
sectionMetas[sectionLookupKey(section.Name, section.Paddr)] = meta
|
||||
for _, hint := range meta.Hints {
|
||||
if hint == nil {
|
||||
continue
|
||||
}
|
||||
result.Hints = append(result.Hints, HintMatch{
|
||||
Bits: hint.Bits,
|
||||
Offset: hint.Offset + section.Paddr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
queryOffsets := make(map[string]struct{}, len(bundle.Functions))
|
||||
for _, fn := range bundle.Functions {
|
||||
queryOffsets[sectionOffsetKey(fn.SectionName, fn.SectionPaddr, fn.SectionOffset)] = struct{}{}
|
||||
sectionKey := sectionLookupKey(fn.SectionName, fn.SectionPaddr)
|
||||
sectionMeta := sectionMetas[sectionKey]
|
||||
|
||||
if sectionMeta != nil && sectionMeta.BinaryID != "" {
|
||||
meta := s.GetLocationSymbolMeta(psk, sectionMeta.BinaryID, fn)
|
||||
if meta != nil && meta.Symbol != nil {
|
||||
result.Symbols = append(result.Symbols, SymbolMatchRecord{
|
||||
Addr: fn.Addr,
|
||||
Symbol: normalizeSymbolRecord(symbolRecordFromProto(meta.Symbol)),
|
||||
Exact: true,
|
||||
MatchedBinaryID: meta.BinaryID,
|
||||
MatchedBy: "exact_location",
|
||||
Offset: fn.SectionPaddr + fn.SectionOffset,
|
||||
Size: hintedSymbolSize(sectionMeta.Hints, fn.SectionOffset, meta.Size),
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
sig := functionSignature(bundle, fn)
|
||||
if sig == nil {
|
||||
continue
|
||||
}
|
||||
meta := s.GetSymbolMeta(psk, sig, nil)
|
||||
if meta != nil && meta.Symbol != nil {
|
||||
result.Symbols = append(result.Symbols, SymbolMatchRecord{
|
||||
Addr: fn.Addr,
|
||||
Symbol: normalizeSymbolRecord(symbolRecordFromProto(meta.Symbol)),
|
||||
Exact: true,
|
||||
MatchedBinaryID: meta.BinaryID,
|
||||
MatchedBy: "exact_signature",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, section := range bundle.Sections {
|
||||
sectionKey := sectionLookupKey(section.Name, section.Paddr)
|
||||
meta := sectionMetas[sectionKey]
|
||||
if meta == nil || meta.BinaryID == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, hint := range meta.Hints {
|
||||
if hint == nil {
|
||||
continue
|
||||
}
|
||||
key := sectionOffsetKey(section.Name, section.Paddr, hint.Offset)
|
||||
if _, exists := queryOffsets[key]; exists {
|
||||
continue
|
||||
}
|
||||
|
||||
locationMeta := DbGetLocationSymbolMetaSafe(s, psk, meta.BinaryID, section.Name, section.Paddr, hint.Offset)
|
||||
if locationMeta == nil || locationMeta.Symbol == nil {
|
||||
continue
|
||||
}
|
||||
result.Symbols = append(result.Symbols, SymbolMatchRecord{
|
||||
Addr: 0,
|
||||
Symbol: normalizeSymbolRecord(symbolRecordFromProto(locationMeta.Symbol)),
|
||||
Exact: true,
|
||||
MatchedBinaryID: meta.BinaryID,
|
||||
MatchedBy: "exact_hint_location",
|
||||
Offset: section.Paddr + hint.Offset,
|
||||
Size: hintedSymbolSize(meta.Hints, hint.Offset, locationMeta.Size),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func DbGetLocationSymbolMetaSafe(s *Server, psk, binaryID, sectionName string, sectionPaddr, sectionOffset uint64) *MetaSymbol {
|
||||
key := FunctionLocationToKey(binaryID, sectionName, sectionPaddr, sectionOffset)
|
||||
for _, db := range s.GetHintsDbs() {
|
||||
meta, err := DbGetLocationSymbolMeta(db, key)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if meta != nil {
|
||||
return meta
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) ShareProgram(psk string, bundle ProgramBundle) ShareProgramResult {
|
||||
bundle = normalizeProgramBundle(bundle)
|
||||
shareBin := &ShareBin{
|
||||
Type: bundle.BinaryType,
|
||||
Os: bundle.OS,
|
||||
Sections: buildShareSections(bundle),
|
||||
}
|
||||
|
||||
for _, fn := range bundle.Functions {
|
||||
sig := functionSignature(bundle, fn)
|
||||
sym := functionSymbol(fn, bundle.Bits)
|
||||
if sig != nil && sym != nil {
|
||||
shareBin.Symbols = append(shareBin.Symbols, &ShareSymbol{
|
||||
Symbol: sym,
|
||||
Signature: sig,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if shareBin.Sections != nil {
|
||||
for _, section := range shareBin.Sections {
|
||||
s.SetHintsWithBinaryID(psk, shareBin, section, bundle.BinaryID)
|
||||
}
|
||||
}
|
||||
if shareBin.Symbols != nil {
|
||||
symbolIndex := 0
|
||||
for _, fn := range bundle.Functions {
|
||||
if functionSignature(bundle, fn) == nil || functionSymbol(fn, bundle.Bits) == nil {
|
||||
continue
|
||||
}
|
||||
share := shareBin.Symbols[symbolIndex]
|
||||
s.SetSymbolWithBinaryID(psk, share.Signature, share.Symbol, bundle.BinaryID)
|
||||
s.SetLocationSymbolWithBinaryID(psk, fn, share.Symbol, bundle.BinaryID)
|
||||
symbolIndex++
|
||||
}
|
||||
}
|
||||
|
||||
return ShareProgramResult{BinaryID: bundle.BinaryID}
|
||||
}
|
||||
+353
@@ -0,0 +1,353 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
func newTestDB(t *testing.T, name string) *bbolt.DB {
|
||||
t.Helper()
|
||||
db, err := OpenDatabase(filepath.Join(t.TempDir(), name))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
db.Close()
|
||||
})
|
||||
return db
|
||||
}
|
||||
|
||||
func TestResolveProgramUsesExactSignatureAndHints(t *testing.T) {
|
||||
db := newTestDB(t, "match.db")
|
||||
section := &SectionHash{Size: 0x30, Paddr: 0x4000, Digest: []byte{0xaa, 0xbb}}
|
||||
sectionKey := SectionHashToKey(section, "elf", "linux")
|
||||
if err := DbSetHintsMeta(db, sectionKey, "psk", ".text", "bin-match", []*Hint{{Bits: 64, Offset: 0x20}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sig := &Signature{Arch: "x86", Bits: 64, Length: 16, Digest: []byte{0x01, 0x02}}
|
||||
if err := DbSetSymbolMeta(db, SignatureToKey(sig), "psk", "bin-match", &Symbol{Name: "sym.good", Signature: "int good()", Callconv: "sysv", Bits: 64}, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
auths: map[string]bool{"psk": true},
|
||||
search: map[string][]*bbolt.DB{GENERIC_DB: {db}},
|
||||
shared: map[string]*bbolt.DB{},
|
||||
}
|
||||
|
||||
result := server.ResolveProgram("psk", ProgramBundle{
|
||||
BinaryType: "elf",
|
||||
OS: "linux",
|
||||
Arch: "x86",
|
||||
Bits: 64,
|
||||
Sections: []SectionRecord{
|
||||
{Name: ".text", Size: 0x30, Paddr: 0x4000, Digest: []byte{0xaa, 0xbb}},
|
||||
},
|
||||
Functions: []FunctionRecord{
|
||||
{Addr: 0x4020, Bits: 64, Arch: "x86", Length: 16, Digest: []byte{0x01, 0x02}},
|
||||
},
|
||||
})
|
||||
|
||||
if len(result.Hints) != 1 || result.Hints[0].Offset != 0x4020 {
|
||||
t.Fatalf("unexpected hints: %#v", result.Hints)
|
||||
}
|
||||
if len(result.Symbols) != 1 {
|
||||
t.Fatalf("unexpected symbols: %#v", result.Symbols)
|
||||
}
|
||||
if !result.Symbols[0].Exact || result.Symbols[0].Symbol.Name != "sym.good" {
|
||||
t.Fatalf("exact symbol mismatch: %#v", result.Symbols[0])
|
||||
}
|
||||
if result.Symbols[0].MatchedBy != "exact_signature" {
|
||||
t.Fatalf("unexpected match type: %#v", result.Symbols[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestShareProgramStoresExactDataWithBinaryID(t *testing.T) {
|
||||
shareDB := newTestDB(t, "share.db")
|
||||
server := &Server{
|
||||
auths: map[string]bool{"psk": true},
|
||||
search: map[string][]*bbolt.DB{GENERIC_DB: {shareDB}},
|
||||
shared: map[string]*bbolt.DB{"psk": shareDB},
|
||||
}
|
||||
|
||||
bundle := ProgramBundle{
|
||||
BinaryType: "elf",
|
||||
OS: "linux",
|
||||
Arch: "x86",
|
||||
Bits: 64,
|
||||
Sections: []SectionRecord{
|
||||
{Name: ".text", Size: 0x40, Paddr: 0x1000, Digest: []byte{0xde, 0xad}},
|
||||
},
|
||||
Functions: []FunctionRecord{
|
||||
{
|
||||
Addr: 0x1010,
|
||||
Size: 16,
|
||||
Bits: 64,
|
||||
Arch: "x86",
|
||||
Length: 16,
|
||||
Digest: []byte{0xca, 0xfe},
|
||||
SectionName: ".text",
|
||||
SectionPaddr: 0x1000,
|
||||
SectionOffset: 0x10,
|
||||
Name: "sym.main",
|
||||
Signature: "int main()",
|
||||
Callconv: "sysv",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := server.ShareProgram("psk", bundle)
|
||||
if result.BinaryID == "" {
|
||||
t.Fatal("share result binary id is empty")
|
||||
}
|
||||
|
||||
normalized := normalizeProgramBundle(bundle)
|
||||
sectionMeta, err := DbGetHintsMeta(shareDB, SectionHashToKey(&SectionHash{
|
||||
Size: normalized.Sections[0].Size,
|
||||
Paddr: normalized.Sections[0].Paddr,
|
||||
Digest: normalized.Sections[0].Digest,
|
||||
}, normalized.BinaryType, normalized.OS))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sectionMeta == nil || sectionMeta.BinaryID != result.BinaryID || len(sectionMeta.Hints) != 1 || sectionMeta.Hints[0].Offset != 0x10 {
|
||||
t.Fatalf("unexpected section meta: %#v", sectionMeta)
|
||||
}
|
||||
|
||||
sig := functionSignature(normalized, normalized.Functions[0])
|
||||
symbolMeta, err := DbGetSymbolMeta(shareDB, SignatureToKey(sig))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if symbolMeta == nil || symbolMeta.BinaryID != result.BinaryID || symbolMeta.Symbol == nil || symbolMeta.Symbol.Name != "sym.main" {
|
||||
t.Fatalf("unexpected symbol meta: %#v", symbolMeta)
|
||||
}
|
||||
|
||||
locationMeta, err := DbGetLocationSymbolMeta(shareDB, FunctionLocationToKey(result.BinaryID, normalized.Functions[0].SectionName, normalized.Functions[0].SectionPaddr, normalized.Functions[0].SectionOffset))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if locationMeta == nil || locationMeta.Symbol == nil || locationMeta.Symbol.Name != "sym.main" {
|
||||
t.Fatalf("unexpected location symbol meta: %#v", locationMeta)
|
||||
}
|
||||
if locationMeta.Size != 16 {
|
||||
t.Fatalf("unexpected location symbol size: %#v", locationMeta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShareProgramPreservesVASectionOffsets(t *testing.T) {
|
||||
shareDB := newTestDB(t, "share-va.db")
|
||||
server := &Server{
|
||||
auths: map[string]bool{"psk": true},
|
||||
search: map[string][]*bbolt.DB{GENERIC_DB: {shareDB}},
|
||||
shared: map[string]*bbolt.DB{"psk": shareDB},
|
||||
}
|
||||
|
||||
bundle := ProgramBundle{
|
||||
BinaryType: "elf",
|
||||
OS: "linux",
|
||||
Arch: "x86",
|
||||
Bits: 64,
|
||||
Sections: []SectionRecord{
|
||||
{Name: ".text", Size: 0x40, Paddr: 0x1000, Digest: []byte{0xde, 0xad}},
|
||||
},
|
||||
Functions: []FunctionRecord{
|
||||
{
|
||||
Addr: 0x401010,
|
||||
Size: 16,
|
||||
Bits: 64,
|
||||
Arch: "x86",
|
||||
Length: 16,
|
||||
Digest: []byte{0xca, 0xfe},
|
||||
SectionName: ".text",
|
||||
SectionPaddr: 0x1000,
|
||||
SectionOffset: 0x10,
|
||||
Name: "sym.main",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
server.ShareProgram("psk", bundle)
|
||||
normalized := normalizeProgramBundle(bundle)
|
||||
sectionMeta, err := DbGetHintsMeta(shareDB, SectionHashToKey(&SectionHash{
|
||||
Size: normalized.Sections[0].Size,
|
||||
Paddr: normalized.Sections[0].Paddr,
|
||||
Digest: normalized.Sections[0].Digest,
|
||||
}, normalized.BinaryType, normalized.OS))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sectionMeta == nil || len(sectionMeta.Hints) != 1 || sectionMeta.Hints[0].Offset != 0x10 {
|
||||
t.Fatalf("unexpected section meta: %#v", sectionMeta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveProgramPrefersExactLocationOverAmbiguousSignature(t *testing.T) {
|
||||
db := newTestDB(t, "location.db")
|
||||
server := &Server{
|
||||
auths: map[string]bool{"psk": true},
|
||||
search: map[string][]*bbolt.DB{GENERIC_DB: {db}},
|
||||
shared: map[string]*bbolt.DB{"psk": db},
|
||||
}
|
||||
|
||||
bundle := normalizeProgramBundle(ProgramBundle{
|
||||
BinaryType: "elf",
|
||||
OS: "linux",
|
||||
Arch: "x86",
|
||||
Bits: 64,
|
||||
Sections: []SectionRecord{
|
||||
{Name: ".text", Size: 0x80, Paddr: 0x1000, Digest: []byte{0xaa, 0xbb}},
|
||||
},
|
||||
Functions: []FunctionRecord{
|
||||
{
|
||||
Addr: 0x1010,
|
||||
Size: 16,
|
||||
Bits: 64,
|
||||
Arch: "x86",
|
||||
Length: 16,
|
||||
Digest: []byte{0xca, 0xfe},
|
||||
SectionName: ".text",
|
||||
SectionPaddr: 0x1000,
|
||||
SectionOffset: 0x10,
|
||||
Name: "sym.real_name",
|
||||
},
|
||||
},
|
||||
})
|
||||
share := server.ShareProgram("psk", bundle)
|
||||
if share.BinaryID == "" {
|
||||
t.Fatal("expected binary id")
|
||||
}
|
||||
|
||||
sig := functionSignature(bundle, bundle.Functions[0])
|
||||
if err := DbSetSymbolMeta(db, SignatureToKey(sig), "psk", share.BinaryID, &Symbol{Name: "sym.wrong_name"}, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
result := server.ResolveProgram("psk", ProgramBundle{
|
||||
BinaryType: "elf",
|
||||
OS: "linux",
|
||||
Arch: "x86",
|
||||
Bits: 64,
|
||||
Sections: []SectionRecord{
|
||||
{Name: ".text", Size: 0x80, Paddr: 0x1000, Digest: []byte{0xaa, 0xbb}},
|
||||
},
|
||||
Functions: []FunctionRecord{
|
||||
{
|
||||
Addr: 0x401010,
|
||||
Bits: 64,
|
||||
Arch: "x86",
|
||||
Length: 16,
|
||||
Digest: []byte{0xca, 0xfe},
|
||||
SectionName: ".text",
|
||||
SectionPaddr: 0x1000,
|
||||
SectionOffset: 0x10,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if len(result.Symbols) != 1 {
|
||||
t.Fatalf("unexpected symbols: %#v", result.Symbols)
|
||||
}
|
||||
if got := result.Symbols[0].Symbol.Name; got != "sym.real_name" {
|
||||
t.Fatalf("expected location symbol, got %q", got)
|
||||
}
|
||||
if got := result.Symbols[0].MatchedBy; got != "exact_location" {
|
||||
t.Fatalf("expected exact_location match, got %q", got)
|
||||
}
|
||||
if got := result.Symbols[0].Offset; got != 0x1010 {
|
||||
t.Fatalf("expected exact offset, got %#x", got)
|
||||
}
|
||||
if got := result.Symbols[0].Size; got != 16 {
|
||||
t.Fatalf("expected exact size, got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveProgramReturnsExtraExactLocationSymbolsForHintedOffsets(t *testing.T) {
|
||||
db := newTestDB(t, "hinted-location.db")
|
||||
server := &Server{
|
||||
auths: map[string]bool{"psk": true},
|
||||
search: map[string][]*bbolt.DB{GENERIC_DB: {db}},
|
||||
shared: map[string]*bbolt.DB{"psk": db},
|
||||
}
|
||||
|
||||
bundle := normalizeProgramBundle(ProgramBundle{
|
||||
BinaryType: "elf",
|
||||
OS: "linux",
|
||||
Arch: "x86",
|
||||
Bits: 64,
|
||||
Sections: []SectionRecord{
|
||||
{Name: ".text", Size: 0x80, Paddr: 0x1000, Digest: []byte{0xaa, 0xbb}},
|
||||
},
|
||||
Functions: []FunctionRecord{
|
||||
{
|
||||
Addr: 0x1010,
|
||||
Size: 16,
|
||||
Bits: 64,
|
||||
Arch: "x86",
|
||||
Length: 16,
|
||||
Digest: []byte{0xca, 0xfe},
|
||||
SectionName: ".text",
|
||||
SectionPaddr: 0x1000,
|
||||
SectionOffset: 0x10,
|
||||
Name: "sym.main",
|
||||
},
|
||||
{
|
||||
Addr: 0x1020,
|
||||
Size: 8,
|
||||
Bits: 64,
|
||||
Arch: "x86",
|
||||
Length: 8,
|
||||
Digest: []byte{0xca, 0xff},
|
||||
SectionName: ".text",
|
||||
SectionPaddr: 0x1000,
|
||||
SectionOffset: 0x20,
|
||||
Name: "sym.cold",
|
||||
},
|
||||
},
|
||||
})
|
||||
share := server.ShareProgram("psk", bundle)
|
||||
if share.BinaryID == "" {
|
||||
t.Fatal("expected binary id")
|
||||
}
|
||||
|
||||
result := server.ResolveProgram("psk", ProgramBundle{
|
||||
BinaryType: "elf",
|
||||
OS: "linux",
|
||||
Arch: "x86",
|
||||
Bits: 64,
|
||||
Sections: []SectionRecord{
|
||||
{Name: ".text", Size: 0x80, Paddr: 0x1000, Digest: []byte{0xaa, 0xbb}},
|
||||
},
|
||||
Functions: []FunctionRecord{
|
||||
{
|
||||
Addr: 0x401010,
|
||||
Bits: 64,
|
||||
Arch: "x86",
|
||||
Length: 16,
|
||||
Digest: []byte{0xca, 0xfe},
|
||||
SectionName: ".text",
|
||||
SectionPaddr: 0x1000,
|
||||
SectionOffset: 0x10,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if len(result.Symbols) != 2 {
|
||||
t.Fatalf("unexpected symbols: %#v", result.Symbols)
|
||||
}
|
||||
if got := result.Symbols[1].Symbol.Name; got != "sym.cold" {
|
||||
t.Fatalf("expected hinted exact location symbol, got %q", got)
|
||||
}
|
||||
if got := result.Symbols[1].MatchedBy; got != "exact_hint_location" {
|
||||
t.Fatalf("expected exact_hint_location match, got %q", got)
|
||||
}
|
||||
if got := result.Symbols[1].Offset; got != 0x1020 {
|
||||
t.Fatalf("expected hinted offset, got %#x", got)
|
||||
}
|
||||
if got := result.Symbols[1].Size; got != 8 {
|
||||
t.Fatalf("expected hinted size, got %d", got)
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,13 @@ var (
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
motd string
|
||||
auths map[string]bool
|
||||
search map[string][]*bbolt.DB
|
||||
shared map[string]*bbolt.DB
|
||||
mutex sync.Mutex
|
||||
queue chan net.Conn
|
||||
motd string
|
||||
auths map[string]bool
|
||||
search map[string][]*bbolt.DB
|
||||
shared map[string]*bbolt.DB
|
||||
mutex sync.Mutex
|
||||
queue chan net.Conn
|
||||
capnpRequireTLS bool
|
||||
}
|
||||
|
||||
func maxThreads() int {
|
||||
@@ -67,7 +68,18 @@ func (s *Server) GetShareDB(key string) *bbolt.DB {
|
||||
return s.shared[key]
|
||||
}
|
||||
|
||||
func (s *Server) GetSymbol(psk string, sig *Signature) *Symbol {
|
||||
func binaryIDAllowed(binaryID string, candidates map[string]struct{}) bool {
|
||||
if len(candidates) < 1 {
|
||||
return true
|
||||
}
|
||||
if binaryID == "" {
|
||||
return true
|
||||
}
|
||||
_, ok := candidates[binaryID]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *Server) GetSymbolMeta(psk string, sig *Signature, candidates map[string]struct{}) *MetaSymbol {
|
||||
if sig == nil || sig.Digest == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -76,18 +88,37 @@ func (s *Server) GetSymbol(psk string, sig *Signature) *Symbol {
|
||||
|
||||
key := SignatureToKey(sig)
|
||||
for _, db := range dbs {
|
||||
sym, err := DbGetSymbol(db, key)
|
||||
meta, err := DbGetSymbolMeta(db, key)
|
||||
if err != nil {
|
||||
log.Error().Str("psk", psk).Err(err).Send()
|
||||
continue
|
||||
} else if sym != nil {
|
||||
return sym
|
||||
} else if meta != nil && binaryIDAllowed(meta.BinaryID, candidates) {
|
||||
return meta
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) SetSymbol(psk string, sig *Signature, sym *Symbol) {
|
||||
func (s *Server) GetLocationSymbolMeta(psk, binaryID string, fn FunctionRecord) *MetaSymbol {
|
||||
if binaryID == "" || fn.SectionName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
key := FunctionLocationToKey(binaryID, fn.SectionName, fn.SectionPaddr, fn.SectionOffset)
|
||||
for _, db := range s.GetHintsDbs() {
|
||||
meta, err := DbGetLocationSymbolMeta(db, key)
|
||||
if err != nil {
|
||||
log.Error().Str("psk", psk).Err(err).Send()
|
||||
continue
|
||||
}
|
||||
if meta != nil {
|
||||
return meta
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) SetSymbolWithBinaryID(psk string, sig *Signature, sym *Symbol, binaryID string) {
|
||||
if sig == nil || sym == nil || sig.Digest == nil || len(sym.Name) < 1 {
|
||||
return
|
||||
}
|
||||
@@ -125,31 +156,84 @@ func (s *Server) SetSymbol(psk string, sig *Signature, sym *Symbol) {
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
err := DbSetSymbol(db, key, psk, sym)
|
||||
err := DbSetSymbolMeta(db, key, psk, binaryID, sym, 0)
|
||||
s.mutex.Unlock()
|
||||
if err != nil {
|
||||
log.Error().Str("psk", psk).Err(err).Send()
|
||||
} else {
|
||||
log.Warn().Str("psk", psk).Str("arch", sig.Arch).Uint32("bits", sig.Bits).Str("name", sym.Name).Send()
|
||||
log.Warn().
|
||||
Str("psk", psk).
|
||||
Str("arch", sig.Arch).
|
||||
Uint32("bits", sig.Bits).
|
||||
Str("name", sym.Name).
|
||||
Str("binary_id", binaryID).
|
||||
Send()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) GetHints(psk string, bin *Binary, sec *SectionHash) []*Hint {
|
||||
func (s *Server) SetLocationSymbolWithBinaryID(psk string, fn FunctionRecord, sym *Symbol, binaryID string) {
|
||||
if binaryID == "" || sym == nil || len(sym.Name) < 1 || fn.SectionName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
db := s.GetShareDB(psk)
|
||||
if db == nil {
|
||||
log.Error().Str("psk", psk).Msg("tried to upload location symbols but db was not found")
|
||||
return
|
||||
}
|
||||
|
||||
key := FunctionLocationToKey(binaryID, fn.SectionName, fn.SectionPaddr, fn.SectionOffset)
|
||||
found, err := DbHasLocationSymbol(db, key)
|
||||
if err != nil {
|
||||
log.Error().Str("psk", psk).Err(err).Send()
|
||||
return
|
||||
}
|
||||
if found {
|
||||
return
|
||||
}
|
||||
|
||||
sym.Name = sanitizeSymbol(sym.Name)
|
||||
sym.Signature = strings.TrimSpace(sym.Signature)
|
||||
sym.Callconv = sanitizeWord(sym.Callconv, "")
|
||||
if sym.Bits > uint32(1024) {
|
||||
sym.Bits = 0
|
||||
}
|
||||
if sym.Name == "" {
|
||||
return
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
err = DbSetLocationSymbolMeta(db, key, psk, binaryID, sym, fn.Size)
|
||||
s.mutex.Unlock()
|
||||
if err != nil {
|
||||
log.Error().Str("psk", psk).Err(err).Send()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) SetSymbol(psk string, sig *Signature, sym *Symbol) {
|
||||
s.SetSymbolWithBinaryID(psk, sig, sym, "")
|
||||
}
|
||||
|
||||
func (s *Server) GetHintsMeta(psk string, binType, binOS string, sec *SectionHash, candidates map[string]struct{}) *MetaHints {
|
||||
if sec == nil || sec.Digest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
dbs := s.GetHintsDbs()
|
||||
key := SectionHashToKey(sec, bin.Type, bin.Os)
|
||||
key := SectionHashToKey(sec, binType, binOS)
|
||||
for _, db := range dbs {
|
||||
hints, err := DbGetHints(db, key)
|
||||
meta, err := DbGetHintsMeta(db, key)
|
||||
if err != nil {
|
||||
log.Error().Str("psk", psk).Err(err).Send()
|
||||
continue
|
||||
} else if hints != nil {
|
||||
return hints
|
||||
} else if meta != nil && binaryIDAllowed(meta.BinaryID, candidates) {
|
||||
return meta
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) SetHints(psk string, bin *ShareBin, sec *ShareSection) {
|
||||
func (s *Server) SetHintsWithBinaryID(psk string, bin *ShareBin, sec *ShareSection, binaryID string) {
|
||||
if sec == nil || sec.Hints == nil || len(sec.Hints) < 1 {
|
||||
return
|
||||
}
|
||||
@@ -173,29 +257,22 @@ func (s *Server) SetHints(psk string, bin *ShareBin, sec *ShareSection) {
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
err := DbSetHints(db, key, psk, sec.Name, sec.Hints)
|
||||
err := DbSetHintsMeta(db, key, psk, sec.Name, binaryID, sec.Hints)
|
||||
s.mutex.Unlock()
|
||||
if err != nil {
|
||||
log.Error().Str("psk", psk).Err(err).Send()
|
||||
} else {
|
||||
log.Warn().Str("psk", psk).Str("section", sec.Name).Int("hints", len(sec.Hints)).Send()
|
||||
log.Warn().
|
||||
Str("psk", psk).
|
||||
Str("section", sec.Name).
|
||||
Int("hints", len(sec.Hints)).
|
||||
Str("binary_id", binaryID).
|
||||
Send()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) IsAuthorized(req *Request) bool {
|
||||
if req.Psk == "" {
|
||||
return false
|
||||
}
|
||||
_, exists := s.auths[req.Psk]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (s *Server) CanShare(req *Request) bool {
|
||||
if req.Psk == "" {
|
||||
return false
|
||||
}
|
||||
canShare, exists := s.auths[req.Psk]
|
||||
return exists && canShare
|
||||
func (s *Server) SetHints(psk string, bin *ShareBin, sec *ShareSection) {
|
||||
s.SetHintsWithBinaryID(psk, bin, sec, "")
|
||||
}
|
||||
|
||||
func (s *Server) Worker() {
|
||||
@@ -234,11 +311,12 @@ func NewServer(config *Config) *Server {
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
motd: config.Message,
|
||||
auths: auths,
|
||||
search: search,
|
||||
shared: shared,
|
||||
queue: make(chan net.Conn, maxQueue),
|
||||
motd: config.Message,
|
||||
auths: auths,
|
||||
search: search,
|
||||
shared: shared,
|
||||
queue: make(chan net.Conn, maxQueue),
|
||||
capnpRequireTLS: config.CapnpRequireTLS,
|
||||
}
|
||||
|
||||
nThreads := maxThreads()
|
||||
|
||||
-1179
File diff suppressed because it is too large.
Load diff
-104
@@ -1,104 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 RizinOrg <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2023 deroad <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// protoc --go_out=. --go_opt=paths=source_relative service.proto
|
||||
|
||||
syntax = "proto3";
|
||||
option go_package = "./main";
|
||||
|
||||
/* generics */
|
||||
|
||||
enum Route {
|
||||
PING = 0;
|
||||
BINARY = 1;
|
||||
SIGNATURE = 2;
|
||||
SHARE_BIN = 3;
|
||||
}
|
||||
|
||||
enum Status {
|
||||
INTERNAL_ERROR = 0;
|
||||
CLIENT_BAD_PRE_SHARED_KEY = 1;
|
||||
CLIENT_NOT_AUTHORIZED = 2;
|
||||
VERSION_MISMATCH = 3;
|
||||
SHARE_WAS_SUCCESSFUL = 4;
|
||||
MESSAGE = 5;
|
||||
HINTS = 6;
|
||||
SYMBOL = 7;
|
||||
}
|
||||
|
||||
message Hint {
|
||||
uint32 bits = 1; // hint function arch bits
|
||||
uint64 offset = 2; // hint function offset
|
||||
}
|
||||
|
||||
message Symbol {
|
||||
string name = 1; // function name
|
||||
string signature = 2; // function signature `type name(type arg0)`
|
||||
string callconv = 3; // function calling convention
|
||||
uint32 bits = 4; // function arch bits
|
||||
}
|
||||
|
||||
/* client messages used to request info to the server */
|
||||
|
||||
message SectionHash {
|
||||
uint32 size = 1; // binary section physical size
|
||||
uint64 paddr = 2; // binary section physical address
|
||||
bytes digest = 3; // binary section digest
|
||||
}
|
||||
|
||||
message Binary {
|
||||
string type = 1; // binary type
|
||||
string os = 2; // binary os
|
||||
repeated SectionHash sections = 3;
|
||||
}
|
||||
|
||||
message Signature {
|
||||
string arch = 1; // architecture name
|
||||
uint32 bits = 2; // architecture bits
|
||||
uint32 length = 3; // function length
|
||||
bytes digest = 4; // function digest of the masked bytes
|
||||
}
|
||||
|
||||
/* client messages used to upload info to the server */
|
||||
|
||||
message ShareSymbol {
|
||||
Symbol symbol = 1; // symbol info
|
||||
Signature signature = 2; // symbol signature
|
||||
}
|
||||
|
||||
message ShareSection {
|
||||
string name = 1; // binary section name
|
||||
SectionHash section = 2; // binary section
|
||||
repeated Hint hints = 3; // binary section hints
|
||||
}
|
||||
|
||||
message ShareBin {
|
||||
string type = 1; // binary type
|
||||
string os = 2; // binary os
|
||||
repeated ShareSection sections = 3; // binary sections
|
||||
repeated ShareSymbol symbols = 4; // binary symbols
|
||||
}
|
||||
|
||||
/* client request generics */
|
||||
|
||||
message Request {
|
||||
string psk = 1; // authorization
|
||||
uint32 version = 2; // protocol version
|
||||
Route route = 3; // protocol route
|
||||
bytes message = 4; // client encoded message based on the route
|
||||
}
|
||||
|
||||
/* server messages used to reply to the client */
|
||||
|
||||
message Message {
|
||||
string text = 1; // server text
|
||||
}
|
||||
|
||||
message MatchHints {
|
||||
repeated Hint hints = 1; // matched section hints
|
||||
}
|
||||
|
||||
message Response {
|
||||
Status status = 1; // server status
|
||||
bytes message = 2; // server encoded message based on the status
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2016 NetDEF, Inc. and contributors
|
||||
# Licensed under the MIT License:
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
@0xc0183dd65ffef0f3;
|
||||
|
||||
annotation nameinfix @0x85a8d86d736ba637 (file): Text;
|
||||
annotation fieldgetset @0xf72bc690355d66de (file): Void;
|
||||
annotation donotinclude @0x8c99797357b357e9 (file): UInt64;
|
||||
annotation typedefto @0xcefaf27713042144 (struct, enum): Text;
|
||||
annotation namespace @0xf2c035025fec7c2b (file): Text;
|
||||
annotation extraheader @0xbadb496d09cf4612 (file): Text;
|
||||
annotation extendedattribute @0xd187bca5c6844c24 (file): Text;
|
||||
@@ -0,0 +1,12 @@
|
||||
@0xd12a1c51fedd6c88;
|
||||
|
||||
annotation package(file) :Text;
|
||||
annotation import(file) :Text;
|
||||
annotation doc(struct, field, enum) :Text;
|
||||
annotation tag(enumerant) :Text;
|
||||
annotation notag(enumerant) :Void;
|
||||
annotation customtype(field) :Text;
|
||||
annotation name(struct, field, union, enum, enumerant, interface, method, param, annotation, const, group) :Text;
|
||||
|
||||
$package("gocp");
|
||||
$import("capnproto.org/go/capnp/v3/std/go");
|
||||
@@ -0,0 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
// Package servicecapnp contains generated Cap'n Proto bindings.
|
||||
//
|
||||
//go:generate go run generate.go
|
||||
package servicecapnp
|
||||
@@ -0,0 +1,121 @@
|
||||
//go:build ignore
|
||||
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
serviceDir, err := schemaDir()
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
|
||||
capnpPath, err := exec.LookPath("capnp")
|
||||
if err != nil {
|
||||
fail(errors.New("capnp compiler was not found on PATH"))
|
||||
}
|
||||
|
||||
capnpcGoPath, err := findCapnpcGo()
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
|
||||
schemaPath := filepath.Join(serviceDir, "service.capnp")
|
||||
cmd := exec.Command(
|
||||
capnpPath,
|
||||
"compile",
|
||||
"--src-prefix="+serviceDir,
|
||||
"-I"+serviceDir,
|
||||
"-o",
|
||||
capnpcGoPath,
|
||||
schemaPath,
|
||||
)
|
||||
cmd.Dir = serviceDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
fail(fmt.Errorf("failed to generate %s: %w", schemaPath, err))
|
||||
}
|
||||
}
|
||||
|
||||
func schemaDir() (string, error) {
|
||||
_, file, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
return "", errors.New("failed to locate generator source file")
|
||||
}
|
||||
return filepath.Dir(file), nil
|
||||
}
|
||||
|
||||
func findCapnpcGo() (string, error) {
|
||||
if path, err := exec.LookPath("capnpc-go"); err == nil {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
exeName := "capnpc-go"
|
||||
if runtime.GOOS == "windows" {
|
||||
exeName += ".exe"
|
||||
}
|
||||
|
||||
for _, dir := range candidateBinDirs() {
|
||||
path := filepath.Join(dir, exeName)
|
||||
if info, err := os.Stat(path); err == nil && !info.IsDir() {
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("capnpc-go was not found on PATH, GOBIN, or GOPATH/bin")
|
||||
}
|
||||
|
||||
func candidateBinDirs() []string {
|
||||
seen := map[string]struct{}{}
|
||||
dirs := []string{}
|
||||
add := func(dir string) {
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
if _, ok := seen[dir]; ok {
|
||||
return
|
||||
}
|
||||
seen[dir] = struct{}{}
|
||||
dirs = append(dirs, dir)
|
||||
}
|
||||
|
||||
add(os.Getenv("GOBIN"))
|
||||
|
||||
if gopath := os.Getenv("GOPATH"); gopath != "" {
|
||||
for _, root := range filepath.SplitList(gopath) {
|
||||
add(filepath.Join(root, "bin"))
|
||||
}
|
||||
}
|
||||
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
add(filepath.Join(home, "go", "bin"))
|
||||
}
|
||||
|
||||
if out, err := exec.Command("go", "env", "GOBIN").Output(); err == nil {
|
||||
add(strings.TrimSpace(string(out)))
|
||||
}
|
||||
if out, err := exec.Command("go", "env", "GOPATH").Output(); err == nil {
|
||||
for _, root := range filepath.SplitList(strings.TrimSpace(string(out))) {
|
||||
add(filepath.Join(root, "bin"))
|
||||
}
|
||||
}
|
||||
|
||||
return dirs
|
||||
}
|
||||
|
||||
func fail(err error) {
|
||||
fmt.Fprintln(os.Stderr, "servicecapnp generator:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
# SPDX-FileCopyrightText: 2026 RizinOrg <[email protected]>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
using C = import "capnp/c.capnp";
|
||||
using Go = import "capnp/go.capnp";
|
||||
|
||||
@0xa0f8f4ce58fdd4d6;
|
||||
|
||||
$C.fieldgetset;
|
||||
$Go.package("servicecapnp");
|
||||
$Go.import("rz-silhouette-server/servicecapnp");
|
||||
|
||||
enum SilCodec {
|
||||
capnp @0;
|
||||
}
|
||||
|
||||
enum SilRoute {
|
||||
ping @0;
|
||||
resolveProgram @1;
|
||||
shareProgram @2;
|
||||
}
|
||||
|
||||
enum SilStatus {
|
||||
internalError @0;
|
||||
clientBadPreSharedKey @1;
|
||||
clientNotAuthorized @2;
|
||||
versionMismatch @3;
|
||||
message @4;
|
||||
serverInfo @5;
|
||||
resolveResult @6;
|
||||
shareResult @7;
|
||||
}
|
||||
|
||||
struct SilSectionHash {
|
||||
size @0 :UInt32;
|
||||
paddr @1 :UInt64;
|
||||
digest @2 :Data;
|
||||
name @3 :Text;
|
||||
}
|
||||
|
||||
struct SilHint {
|
||||
bits @0 :UInt32;
|
||||
offset @1 :UInt64;
|
||||
}
|
||||
|
||||
struct SilSymbol {
|
||||
name @0 :Text;
|
||||
signature @1 :Text;
|
||||
callconv @2 :Text;
|
||||
bits @3 :UInt32;
|
||||
}
|
||||
|
||||
struct SilFunctionBundle {
|
||||
addr @0 :UInt64;
|
||||
size @1 :UInt32;
|
||||
bits @2 :UInt32;
|
||||
arch @3 :Text;
|
||||
length @4 :UInt32;
|
||||
digest @5 :Data;
|
||||
sectionName @6 :Text;
|
||||
sectionPaddr @7 :UInt64;
|
||||
name @8 :Text;
|
||||
signature @9 :Text;
|
||||
callconv @10 :Text;
|
||||
sectionOffset @11 :UInt64;
|
||||
}
|
||||
|
||||
struct SilProgramBundle {
|
||||
binaryType @0 :Text;
|
||||
os @1 :Text;
|
||||
arch @2 :Text;
|
||||
bits @3 :UInt32;
|
||||
binaryId @4 :Text;
|
||||
sections @5 :List(SilSectionHash);
|
||||
functions @6 :List(SilFunctionBundle);
|
||||
}
|
||||
|
||||
struct SilPing {
|
||||
reserved @0 :Bool;
|
||||
}
|
||||
|
||||
struct SilResolveProgram {
|
||||
program @0 :SilProgramBundle;
|
||||
}
|
||||
|
||||
struct SilShareProgram {
|
||||
program @0 :SilProgramBundle;
|
||||
}
|
||||
|
||||
struct SilRequest {
|
||||
psk @0 :Text;
|
||||
version @1 :UInt32;
|
||||
route @2 :SilRoute;
|
||||
|
||||
union {
|
||||
ping @3 :SilPing;
|
||||
resolveProgram @4 :SilResolveProgram;
|
||||
shareProgram @5 :SilShareProgram;
|
||||
}
|
||||
}
|
||||
|
||||
struct SilMessage {
|
||||
text @0 :Text;
|
||||
}
|
||||
|
||||
struct SilServerInfo {
|
||||
supportedCodecs @0 :List(SilCodec);
|
||||
version @1 :UInt32;
|
||||
tlsRequired @2 :Bool;
|
||||
}
|
||||
|
||||
struct SilSymbolMatch {
|
||||
addr @0 :UInt64;
|
||||
symbol @1 :SilSymbol;
|
||||
exact @2 :Bool;
|
||||
matchedBinaryId @3 :Text;
|
||||
matchedBy @4 :Text;
|
||||
offset @5 :UInt64;
|
||||
size @6 :UInt32;
|
||||
}
|
||||
|
||||
struct SilResolveResult {
|
||||
hints @0 :List(SilHint);
|
||||
symbols @1 :List(SilSymbolMatch);
|
||||
}
|
||||
|
||||
struct SilShareResult {
|
||||
binaryId @0 :Text;
|
||||
}
|
||||
|
||||
struct SilResponse {
|
||||
status @0 :SilStatus;
|
||||
|
||||
union {
|
||||
message @1 :SilMessage;
|
||||
serverInfo @2 :SilServerInfo;
|
||||
resolveResult @3 :SilResolveResult;
|
||||
shareResult @4 :SilShareResult;
|
||||
}
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
1007500102010111054a110801ff64656d6f2d70736b000001
|
||||
@@ -0,0 +1,252 @@
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
PROTOCOL_VERSION = uint32(1)
|
||||
)
|
||||
|
||||
type Hint struct {
|
||||
Bits uint32 `json:"bits"`
|
||||
Offset uint64 `json:"offset"`
|
||||
}
|
||||
|
||||
type Symbol struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Signature string `json:"signature,omitempty"`
|
||||
Callconv string `json:"callconv,omitempty"`
|
||||
Bits uint32 `json:"bits,omitempty"`
|
||||
}
|
||||
|
||||
type SectionHash struct {
|
||||
Size uint32 `json:"size"`
|
||||
Paddr uint64 `json:"paddr"`
|
||||
Digest []byte `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
type Signature struct {
|
||||
Arch string `json:"arch,omitempty"`
|
||||
Bits uint32 `json:"bits,omitempty"`
|
||||
Length uint32 `json:"length,omitempty"`
|
||||
Digest []byte `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
type ShareSymbol struct {
|
||||
Symbol *Symbol `json:"symbol,omitempty"`
|
||||
Signature *Signature `json:"signature,omitempty"`
|
||||
}
|
||||
|
||||
type ShareSection struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Section *SectionHash `json:"section,omitempty"`
|
||||
Hints []*Hint `json:"hints,omitempty"`
|
||||
}
|
||||
|
||||
type ShareBin struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Os string `json:"os,omitempty"`
|
||||
Sections []*ShareSection `json:"sections,omitempty"`
|
||||
Symbols []*ShareSymbol `json:"symbols,omitempty"`
|
||||
}
|
||||
|
||||
type SymbolRecord struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Signature string `json:"signature,omitempty"`
|
||||
Callconv string `json:"callconv,omitempty"`
|
||||
Bits uint32 `json:"bits,omitempty"`
|
||||
}
|
||||
|
||||
type SectionRecord struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Size uint32 `json:"size"`
|
||||
Paddr uint64 `json:"paddr"`
|
||||
Digest []byte `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
type FunctionRecord struct {
|
||||
Addr uint64 `json:"addr"`
|
||||
Size uint32 `json:"size"`
|
||||
Bits uint32 `json:"bits,omitempty"`
|
||||
Arch string `json:"arch,omitempty"`
|
||||
Length uint32 `json:"length,omitempty"`
|
||||
Digest []byte `json:"digest,omitempty"`
|
||||
SectionName string `json:"section_name,omitempty"`
|
||||
SectionPaddr uint64 `json:"section_paddr"`
|
||||
SectionOffset uint64 `json:"section_offset"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Signature string `json:"signature,omitempty"`
|
||||
Callconv string `json:"callconv,omitempty"`
|
||||
}
|
||||
|
||||
type ProgramBundle struct {
|
||||
BinaryType string `json:"binary_type,omitempty"`
|
||||
OS string `json:"os,omitempty"`
|
||||
Arch string `json:"arch,omitempty"`
|
||||
Bits uint32 `json:"bits,omitempty"`
|
||||
BinaryID string `json:"binary_id,omitempty"`
|
||||
Sections []SectionRecord `json:"sections,omitempty"`
|
||||
Functions []FunctionRecord `json:"functions,omitempty"`
|
||||
}
|
||||
|
||||
type HintMatch struct {
|
||||
Bits uint32 `json:"bits"`
|
||||
Offset uint64 `json:"offset"`
|
||||
}
|
||||
|
||||
type SymbolMatchRecord struct {
|
||||
Addr uint64 `json:"addr"`
|
||||
Symbol SymbolRecord `json:"symbol"`
|
||||
Exact bool `json:"exact"`
|
||||
MatchedBinaryID string `json:"matched_binary_id,omitempty"`
|
||||
MatchedBy string `json:"matched_by,omitempty"`
|
||||
Offset uint64 `json:"offset,omitempty"`
|
||||
Size uint32 `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
type ResolveProgramResult struct {
|
||||
Hints []HintMatch `json:"hints,omitempty"`
|
||||
Symbols []SymbolMatchRecord `json:"symbols,omitempty"`
|
||||
}
|
||||
|
||||
type ShareProgramResult struct {
|
||||
BinaryID string `json:"binary_id,omitempty"`
|
||||
}
|
||||
|
||||
func symbolRecordFromProto(sym *Symbol) SymbolRecord {
|
||||
if sym == nil {
|
||||
return SymbolRecord{}
|
||||
}
|
||||
return SymbolRecord{
|
||||
Name: sym.Name,
|
||||
Signature: sym.Signature,
|
||||
Callconv: sym.Callconv,
|
||||
Bits: sym.Bits,
|
||||
}
|
||||
}
|
||||
|
||||
func (s SymbolRecord) Proto() *Symbol {
|
||||
if s == (SymbolRecord{}) {
|
||||
return nil
|
||||
}
|
||||
return &Symbol{
|
||||
Name: s.Name,
|
||||
Signature: s.Signature,
|
||||
Callconv: s.Callconv,
|
||||
Bits: s.Bits,
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeSymbolRecord(sym SymbolRecord) SymbolRecord {
|
||||
sym.Name = sanitizeSymbol(sym.Name)
|
||||
sym.Signature = strings.TrimSpace(sym.Signature)
|
||||
sym.Callconv = sanitizeWord(sym.Callconv, "")
|
||||
if sym.Bits > 1024 {
|
||||
sym.Bits = 0
|
||||
}
|
||||
return sym
|
||||
}
|
||||
|
||||
func normalizeProgramBundle(bundle ProgramBundle) ProgramBundle {
|
||||
bundle.BinaryType = sanitizeWord(bundle.BinaryType, GENERIC_DB)
|
||||
bundle.OS = sanitizeWord(bundle.OS, GENERIC_DB)
|
||||
bundle.Arch = sanitizeWord(bundle.Arch, GENERIC_DB)
|
||||
for i := range bundle.Sections {
|
||||
bundle.Sections[i].Name = strings.TrimSpace(bundle.Sections[i].Name)
|
||||
bundle.Sections[i].Digest = append([]byte(nil), bundle.Sections[i].Digest...)
|
||||
}
|
||||
for i := range bundle.Functions {
|
||||
fn := &bundle.Functions[i]
|
||||
fn.Arch = sanitizeWord(fn.Arch, bundle.Arch)
|
||||
fn.SectionName = strings.TrimSpace(fn.SectionName)
|
||||
fn.Name = strings.TrimSpace(fn.Name)
|
||||
fn.Signature = strings.TrimSpace(fn.Signature)
|
||||
fn.Callconv = sanitizeWord(fn.Callconv, "")
|
||||
fn.Digest = append([]byte(nil), fn.Digest...)
|
||||
if fn.Length < 1 {
|
||||
fn.Length = fn.Size
|
||||
}
|
||||
if fn.Bits < 1 {
|
||||
fn.Bits = bundle.Bits
|
||||
}
|
||||
}
|
||||
if bundle.BinaryID == "" {
|
||||
bundle.BinaryID = computeBinaryID(bundle)
|
||||
}
|
||||
return bundle
|
||||
}
|
||||
|
||||
func computeBinaryID(bundle ProgramBundle) string {
|
||||
type manifestSection struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Size uint32 `json:"size"`
|
||||
Paddr uint64 `json:"paddr"`
|
||||
Digest string `json:"digest,omitempty"`
|
||||
}
|
||||
type manifestFunction struct {
|
||||
Addr uint64 `json:"addr"`
|
||||
Size uint32 `json:"size"`
|
||||
Bits uint32 `json:"bits,omitempty"`
|
||||
Arch string `json:"arch,omitempty"`
|
||||
Length uint32 `json:"length,omitempty"`
|
||||
Digest string `json:"digest,omitempty"`
|
||||
SectionName string `json:"section_name,omitempty"`
|
||||
SectionPaddr uint64 `json:"section_paddr"`
|
||||
SectionOffset uint64 `json:"section_offset"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Signature string `json:"signature,omitempty"`
|
||||
Callconv string `json:"callconv,omitempty"`
|
||||
}
|
||||
type manifest struct {
|
||||
BinaryType string `json:"binary_type,omitempty"`
|
||||
OS string `json:"os,omitempty"`
|
||||
Arch string `json:"arch,omitempty"`
|
||||
Bits uint32 `json:"bits,omitempty"`
|
||||
Sections []manifestSection `json:"sections,omitempty"`
|
||||
Functions []manifestFunction `json:"functions,omitempty"`
|
||||
}
|
||||
|
||||
out := manifest{
|
||||
BinaryType: bundle.BinaryType,
|
||||
OS: bundle.OS,
|
||||
Arch: bundle.Arch,
|
||||
Bits: bundle.Bits,
|
||||
Sections: make([]manifestSection, 0, len(bundle.Sections)),
|
||||
Functions: make([]manifestFunction, 0, len(bundle.Functions)),
|
||||
}
|
||||
|
||||
for _, sec := range bundle.Sections {
|
||||
out.Sections = append(out.Sections, manifestSection{
|
||||
Name: sec.Name,
|
||||
Size: sec.Size,
|
||||
Paddr: sec.Paddr,
|
||||
Digest: hex.EncodeToString(sec.Digest),
|
||||
})
|
||||
}
|
||||
for _, fn := range bundle.Functions {
|
||||
out.Functions = append(out.Functions, manifestFunction{
|
||||
Addr: fn.Addr,
|
||||
Size: fn.Size,
|
||||
Bits: fn.Bits,
|
||||
Arch: fn.Arch,
|
||||
Length: fn.Length,
|
||||
Digest: hex.EncodeToString(fn.Digest),
|
||||
SectionName: fn.SectionName,
|
||||
SectionPaddr: fn.SectionPaddr,
|
||||
SectionOffset: fn.SectionOffset,
|
||||
Name: fn.Name,
|
||||
Signature: fn.Signature,
|
||||
Callconv: fn.Callconv,
|
||||
})
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(out)
|
||||
sum := sha256.Sum256(body)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
SYMBOLFILTER = regexp.MustCompile(`[^a-zA-Z0-9:.]+`)
|
||||
SYMBOLFILTER = regexp.MustCompile(`[^a-zA-Z0-9:._]+`)
|
||||
NONALPHANUMERIC = regexp.MustCompile(`[^a-z0-9]+`)
|
||||
AUTH_FAIL = errors.New("auth: failed to authenticate")
|
||||
TCP_PKT_TOO_BIG = errors.New("tcp: packet size is too big")
|
||||
|
||||
Reference in new issue
Block a user