From 792a1566477b264889ea6516cf93001993366c99 Mon Sep 17 00:00:00 2001 From: springzfx Date: Sat, 23 May 2020 05:02:05 +0800 Subject: [PATCH] add execsnoop --- CMakeLists.txt | 3 +- cgproxy.service | 2 +- config.json | 8 +-- execsnoop.py | 133 +++++++++++++++++++++++++++++++++++++++++++++++ src/cgproxy.hpp | 13 ++--- src/cgproxyd.hpp | 28 ++++++++-- src/common.h | 5 +- src/config.cpp | 9 ++++ src/config.h | 2 + 9 files changed, 187 insertions(+), 16 deletions(-) create mode 100644 execsnoop.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 76f45df..f942635 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,8 @@ install(FILES cgproxyd DESTINATION /usr/bin PERMISSIONS ${basic_permission}) install(FILES cgnoproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission}) install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/) install(FILES config.json DESTINATION /etc/cgproxy/) -install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/) +install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/ PERMISSIONS ${basic_permission}) +install(FILES execsnoop.py DESTINATION /usr/share/cgproxy/scripts/ PERMISSIONS ${basic_permission}) install(FILES readme.md DESTINATION /usr/share/doc/cgproxy/) # man pages diff --git a/cgproxy.service b/cgproxy.service index fb040ec..9a1baaa 100644 --- a/cgproxy.service +++ b/cgproxy.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=simple -ExecStart=/usr/bin/cgproxyd +ExecStart=/usr/bin/cgproxyd --execsnoop [Install] WantedBy=multi-user.target diff --git a/config.json b/config.json index 5fd161b..6e09866 100644 --- a/config.json +++ b/config.json @@ -1,11 +1,13 @@ { + "port": 12345, + "program_noproxy": ["/usr/lib/v2ray/v2ray", "/usr/bin/qv2ray"], + "program_proxy": [], "cgroup_noproxy": ["/system.slice/v2ray.service"], "cgroup_proxy": [], - "enable_dns": true, "enable_gateway": false, + "enable_dns": true, "enable_ipv4": true, "enable_ipv6": true, "enable_tcp": true, - "enable_udp": true, - "port": 12345 + "enable_udp": true } diff --git a/execsnoop.py b/execsnoop.py new file mode 100644 index 0000000..b6389c2 --- /dev/null +++ b/execsnoop.py @@ -0,0 +1,133 @@ +#!/usr/bin/python +# This won't catch all new processes: an application may fork() but not exec(). + +from __future__ import print_function +from bcc import BPF +from bcc.utils import ArgString, printb +import bcc.utils as utils +import argparse +import re +import time +from collections import defaultdict +import os +import sys +import signal +import time +import shutil + +# define BPF program +bpf_text = """ +#include +#include +#include + +#define ARGSIZE 256 + +struct data_t { + u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel) + char path[ARGSIZE]; + int retval; +}; + +BPF_PERF_OUTPUT(events); + +int syscall__execve(struct pt_regs *ctx, + const char __user *filename, + const char __user *const __user *__argv, + const char __user *const __user *__envp) +{ + // create data here and pass to submit_arg to save stack space (#555) + struct data_t data = {}; + struct task_struct *task; + + data.pid = bpf_get_current_pid_tgid() >> 32; + bpf_probe_read(data.path, sizeof(data.path), filename); + events.perf_submit(ctx, &data, sizeof(struct data_t)); + + return 0; +} +""" + +def getRealPath(exec_path): + path=exec_path.strip() + if not path.startswith("/"): + path=shutil.which(path) + if path: + path=os.path.realpath(path) + if path and os.path.isfile(path): + return path + + print("'{0}' can not be find".format(exec_path)) + +def getParam(): + global exec_path_proxy, exec_path_noproxy + exec_path_str=os.getenv('program_proxy') + if exec_path_str: + paths=exec_path_str.split(':') + exec_path_proxy=[getRealPath(x) for x in paths] + print("program with proxy:", end =" ") + print(*exec_path_proxy,flush=True) + + exec_path_str=os.getenv('program_noproxy') + if exec_path_str: + paths=exec_path_str.split(':') + exec_path_noproxy=[getRealPath(x) for x in paths] + print("program without proxy:", end =" ") + print(*exec_path_noproxy, flush=True) + +def exit_gracefully(signum, frame): + print("execsnoop receive signal: {0}".format(signum),flush=True) + sys.exit(0) + +def attach(pid, path, proxy=True): + if proxy: + print("proxy: %-6d %s" % (pid, path),flush=True) + os.system("/usr/bin/cgproxy --pid {0}".format(pid)) + else: + print("noproxy: %-6d %s" % (pid, path),flush=True) + os.system("/usr/bin/cgproxy --pid {0} --noproxy".format(pid)) + +def processAlreadyRunning(): + from subprocess import check_output + def get_pid(name): + return map(int,check_output(["pidof",name]).split()) + global exec_path_proxy, exec_path_noproxy + for path in exec_path_proxy: + for pid in get_pid(path): + attach(pid,path,True) + for path in exec_path_noproxy: + for pid in get_pid(path): + attach(pid,path,False) + +signal.signal(signal.SIGINT, exit_gracefully) +signal.signal(signal.SIGHUP, exit_gracefully) +signal.signal(signal.SIGTERM, exit_gracefully) + +show_ignore=False +exec_path_proxy=[] +exec_path_noproxy=[] +getParam() +processAlreadyRunning() + +# initialize BPF +b = BPF(text=bpf_text) +execve_fnname = b.get_syscall_fnname("execve") +b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve") + +# process event +def print_event(cpu, data, size): + event = b["events"].event(data) + pid=event.pid + exec_path=event.path.decode('utf-8') + if (exec_path in exec_path_noproxy): + attach(pid, exec_path, False) + elif (exec_path in exec_path_proxy): + attach(pid, exec_path, True) + elif (show_ignore): + print("ignore: %-6d %s" % (pid, exec_path),flush=True) + + +# loop with callback to print_event +b["events"].open_perf_buffer(print_event) +while 1: + b.perf_buffer_poll() \ No newline at end of file diff --git a/src/cgproxy.hpp b/src/cgproxy.hpp index 5243713..da185d8 100644 --- a/src/cgproxy.hpp +++ b/src/cgproxy.hpp @@ -11,7 +11,8 @@ using namespace CGPROXY::CONFIG; namespace CGPROXY::CGPROXY { bool print_help = false, proxy = true; -bool attach_pid=false; string arg_pid; +bool attach_pid = false; +string arg_pid; inline void print_usage() { if (proxy) { cout << "Run program with proxy" << endl; @@ -29,8 +30,8 @@ bool processArgs(const int argc, char *argv[], int &shift) { if (strcmp(argv[i], "--pid") == 0) { attach_pid = true; i++; - if (i==argc) return false; - arg_pid=argv[i]; + if (i == argc) return false; + arg_pid = argv[i]; if (!validPid(arg_pid)) return false; continue; } @@ -52,7 +53,7 @@ void send_pid(const pid_t pid, bool proxy, int &status) { int main(int argc, char *argv[]) { int shift = -1; - if (!processArgs(argc, argv, shift)){ + if (!processArgs(argc, argv, shift)) { error("parameter error"); exit(EXIT_FAILURE); } @@ -68,10 +69,10 @@ int main(int argc, char *argv[]) { } int status = -1; - send_pid(attach_pid?stoi(arg_pid):getpid(), proxy, status); + send_pid(attach_pid ? stoi(arg_pid) : getpid(), proxy, status); if (status != 0) { error("attach process failed"); - if (status==1) error("maybe cgproxy.service not running"); + if (status == 1) error("maybe cgproxy.service not running"); exit(EXIT_FAILURE); } diff --git a/src/cgproxyd.hpp b/src/cgproxyd.hpp index af633dd..706e7ec 100644 --- a/src/cgproxyd.hpp +++ b/src/cgproxyd.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -19,10 +20,14 @@ using namespace ::CGPROXY::CGROUP; namespace CGPROXY::CGPROXYD { +bool print_help = false; +bool enable_execsnoop = false; + class cgproxyd { thread_arg arg_t; Config config; pthread_t socket_thread_id = -1; + pid_t exec_snoop_pid = -1; static cgproxyd *instance; static int handle_msg_static(char *msg) { @@ -40,7 +45,7 @@ class cgproxyd { } else { instance->stop(); } - exit(signum); + exit(0); } // single process instance @@ -113,6 +118,22 @@ class cgproxyd { return thread_id; } + void startExecSnoopProc() { + if (exec_snoop_pid != -1){ + kill(exec_snoop_pid, SIGINT); + exec_snoop_pid=-1; + } + pid_t pid = fork(); + if (pid == 0) { + execl(BPF_EXEC_SNOOP_START, (char *) NULL); + exit(0); + } else if (pid<0){ + error("fork precess failed"); + }else { + exec_snoop_pid = pid; + } + } + void assignStaticInstance() { instance = this; } public: @@ -134,6 +155,7 @@ public: system(TPROXY_IPTABLS_CLEAN); c->toEnv(); system(TPROXY_IPTABLS_START); + if (enable_execsnoop) startExecSnoopProc(); // no need to track running status return 0; } @@ -141,6 +163,7 @@ public: void stop() { debug("stopping"); system(TPROXY_IPTABLS_CLEAN); + // if (exec_snoop_pid != -1) kill(exec_snoop_pid, SIGINT); unlock(); } @@ -149,8 +172,6 @@ public: cgproxyd *cgproxyd::instance = NULL; -bool print_help = false; - void print_usage() { cout << "Start a daemon with unix socket to accept control" << endl; cout << "Usage: cgproxyd [--help] [--debug]" << endl; @@ -161,6 +182,7 @@ void processArgs(const int argc, char *argv[]) { for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--debug") == 0) { enable_debug = true; } if (strcmp(argv[i], "--help") == 0) { print_help = true; } + if (strcmp(argv[i], "--execsnoop") == 0) { enable_execsnoop = true; } if (argv[i][0] != '-') { break; } } } diff --git a/src/common.h b/src/common.h index 207e6e2..88b8820 100644 --- a/src/common.h +++ b/src/common.h @@ -7,8 +7,9 @@ #include using namespace std; -#define TPROXY_IPTABLS_START "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh" -#define TPROXY_IPTABLS_CLEAN "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop" +#define TPROXY_IPTABLS_START "/usr/share/cgproxy/scripts/cgroup-tproxy.sh" +#define TPROXY_IPTABLS_CLEAN "/usr/share/cgproxy/scripts/cgroup-tproxy.sh stop" +#define BPF_EXEC_SNOOP_START "/usr/share/cgproxy/scripts/execsnoop.py" #define PID_LOCK_FILE "/var/run/cgproxyd.pid" #define SOCKET_PATH "/tmp/cgproxy_unix_socket" diff --git a/src/config.cpp b/src/config.cpp index a057f89..088abb5 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -21,6 +21,8 @@ namespace CGPROXY::CONFIG { void Config::toEnv() { mergeReserved(); + setenv("program_proxy", join2str(program_proxy, ':').c_str(), 1); + setenv("program_noproxy", join2str(program_noproxy, ':').c_str(), 1); setenv("cgroup_proxy", join2str(cgroup_proxy, ':').c_str(), 1); setenv("cgroup_noproxy", join2str(cgroup_noproxy, ':').c_str(), 1); setenv("enable_gateway", to_str(enable_gateway).c_str(), 1); @@ -43,6 +45,8 @@ int Config::saveToFile(const string f) { string Config::toJsonStr() { json j; + add2json(program_proxy); + add2json(program_noproxy); add2json(cgroup_proxy); add2json(cgroup_noproxy); add2json(enable_gateway); @@ -74,6 +78,8 @@ int Config::loadFromJsonStr(const string js) { return PARAM_ERROR; } json j = json::parse(js); + tryassign(program_proxy); + tryassign(program_noproxy); tryassign(cgroup_proxy); tryassign(cgroup_noproxy); tryassign(enable_gateway); @@ -96,6 +102,7 @@ bool Config::validateJsonStr(const string js) { bool status = true; const set boolset = {"enable_gateway", "enable_dns", "enable_tcp", "enable_udp", "enable_ipv4", "enable_ipv6"}; + const set allowset = {"program_proxy", "program_noproxy"}; for (auto &[key, value] : j.items()) { if (key == "cgroup_proxy" || key == "cgroup_noproxy") { if (value.is_string() && !validCgroup((string)value)) status = false; @@ -106,6 +113,8 @@ bool Config::validateJsonStr(const string js) { if (!validPort(value)) status = false; } else if (boolset.find(key) != boolset.end()) { if (!value.is_boolean()) status = false; + } else if (allowset.find(key) != allowset.end()) { + } else { error("unknown key: %s", key.c_str()); return false; diff --git a/src/config.h b/src/config.h index 246cbfc..399ecfc 100644 --- a/src/config.h +++ b/src/config.h @@ -13,6 +13,8 @@ public: const string cgroup_proxy_preserved = CGROUP_PROXY_PRESVERED; const string cgroup_noproxy_preserved = CGROUP_NOPROXY_PRESVERED; + vector program_proxy; + vector program_noproxy; vector cgroup_proxy; vector cgroup_noproxy; bool enable_gateway = false;