summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authordaniel <daniel@planethacker.net>2025-05-06 16:57:32 -0700
committerdaniel <daniel@planethacker.net>2025-05-06 16:57:32 -0700
commit2278df1493e064c197913e49b5d1935942d83448 (patch)
tree42f06ab2f76e2ddf228bafbb03f79621975a4534 /include
initial import
Diffstat (limited to 'include')
-rw-r--r--include/agent_context.h23
-rw-r--r--include/aho-corasick.h30
-rw-r--r--include/av_rules.h86
-rw-r--r--include/base64.h4
-rw-r--r--include/djb2.h5
-rw-r--r--include/dns.h15
-rw-r--r--include/entropy.h12
-rw-r--r--include/error.h8
-rw-r--r--include/fanotify.h6
-rw-r--r--include/hash_ledger.h59
-rw-r--r--include/json.h54
-rw-r--r--include/md5.h6
-rw-r--r--include/net.h9
-rw-r--r--include/output.h4
-rw-r--r--include/proc.h49
-rw-r--r--include/proc_connector.h24
-rw-r--r--include/proc_ledger.h61
-rw-r--r--include/sha256.h40
-rw-r--r--include/sniffer.h44
-rw-r--r--include/string_common.h6
-rw-r--r--include/time_common.h4
21 files changed, 549 insertions, 0 deletions
diff --git a/include/agent_context.h b/include/agent_context.h
new file mode 100644
index 0000000..01bd3d0
--- /dev/null
+++ b/include/agent_context.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <limits.h>
+
+//#include <linux/limits.h>
+
+#include "proc_ledger.h"
+#include "hash_ledger.h"
+#include "aho-corasick.h"
+#include "av_rules.h"
+
+struct agent_context {
+ struct proc_ledger *proc_ledger;
+ struct hash_ledger *hash_ledger;
+ rule_set_t rules;
+ ac_context_t *ac;
+ char hostname[HOST_NAME_MAX];
+ long ticks;
+ unsigned long boot_time;
+ size_t maxsize;
+};
+
+typedef struct agent_context agent_context_t;
diff --git a/include/aho-corasick.h b/include/aho-corasick.h
new file mode 100644
index 0000000..a468256
--- /dev/null
+++ b/include/aho-corasick.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define AC_BUF_SIZE 65536
+#define AC_ALPHABET_SIZE 256
+
+// forward declaration to allow users to define context
+struct ac_context;
+
+typedef struct ac_match {
+ const char *id;
+ size_t offset;
+ size_t len;
+} ac_match_t;
+
+typedef struct ac_context ac_context_t;
+typedef struct pattern_node ac_node_t;
+typedef struct pattern_entry ac_pattern_t;
+
+typedef void (*ac_callback)(const ac_match_t *match, void *user_data);
+
+ac_context_t *ac_new(void);
+void ac_free(ac_context_t *ctx);
+int ac_add_pattern(ac_context_t *ctx, const char *id, const uint8_t *pattern, size_t len);
+int ac_build(ac_context_t *ctx);
+int ac_match(ac_context_t *ctx, const uint8_t *data, size_t len, ac_callback cb, void *user_data);
+int ac_match_fd(ac_context_t *ctx, int fd, ac_callback cb, void *user_data);
+int ac_match_path(ac_context_t *ctx, const char *path, ac_callback cb, void *user_data);
diff --git a/include/av_rules.h b/include/av_rules.h
new file mode 100644
index 0000000..8e55a54
--- /dev/null
+++ b/include/av_rules.h
@@ -0,0 +1,86 @@
+#pragma once
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define MAX_RULE_CONDITIONS 16
+#define MAX_RULES 128
+#define MAX_PATTERNS 512
+#define MAX_MATCH_OFFSETS 64
+#define PATTERN_TABLE_SIZE 1024
+
+typedef struct {
+ const char *id;
+ uint8_t *bytes;
+ size_t len;
+
+ size_t match_offsets[MAX_MATCH_OFFSETS];
+ size_t match_count;
+} pattern_t;
+
+/* typedef struct { */
+/* const char *id; */
+/* pattern_t *entry; */
+/* } pattern_bucket_t; */
+
+typedef struct pattern_bucket {
+ const char *id;
+ pattern_t *entry;
+ struct pattern_bucket *next;
+} pattern_bucket_t;
+
+//pattern_bucket_t *buckets[PATTERN_TABLE_SIZE];
+
+typedef struct {
+ pattern_t patterns[MAX_PATTERNS];
+ size_t count;
+
+ pattern_bucket_t *buckets[PATTERN_TABLE_SIZE];
+} pattern_table_t;
+
+
+typedef enum {
+ RULE_BLOCK,
+ RULE_ALLOW,
+ RULE_QUARANTINE,
+ RULE_INFORMATIONAL
+} rule_action_t;
+
+typedef enum {
+ COND_TYPE_REQUIRED, // and
+ COND_TYPE_OPTIONAL, // or
+ COND_TYPE_NEGATED // not
+} condition_type_t;
+
+typedef struct {
+ const char *pattern_id;
+ size_t offset;
+ bool has_offset;
+ condition_type_t type;
+} rule_condition_t;
+
+typedef struct {
+ const char *id;
+ rule_action_t action;
+ rule_condition_t conditions[MAX_RULE_CONDITIONS];
+ size_t condition_count;
+} rule_t;
+
+typedef struct {
+ rule_t rules[MAX_RULES];
+ size_t rule_count;
+ pattern_table_t patterns;
+} rule_set_t;
+
+
+pattern_t *pattern_table_find(pattern_table_t *table, const char *id);
+int pattern_table_add(pattern_table_t *table, const char *id, const uint8_t *bytes, size_t len);
+void pattern_table_clear_matches(pattern_table_t *table);
+void pattern_table_free(pattern_table_t *table);
+
+int load_rules(const char *path, rule_set_t *out_ruleset);
+rule_action_t parse_action(const char *s);
+bool evaluate_rule(const rule_t *rule, pattern_table_t *patterns);
+void dump_rule(const rule_t *r);
+void free_rules(rule_set_t *rules);
diff --git a/include/base64.h b/include/base64.h
new file mode 100644
index 0000000..7938980
--- /dev/null
+++ b/include/base64.h
@@ -0,0 +1,4 @@
+#pragma once
+
+unsigned char *base64_encode(const unsigned char *, size_t, size_t *);
+unsigned char *base64_decode(const unsigned char *, size_t, size_t *);
diff --git a/include/djb2.h b/include/djb2.h
new file mode 100644
index 0000000..fda01f9
--- /dev/null
+++ b/include/djb2.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <stdint.h>
+
+uint32_t djb2(const char *str);
diff --git a/include/dns.h b/include/dns.h
new file mode 100644
index 0000000..eb8f125
--- /dev/null
+++ b/include/dns.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define MAX_DNS_NAME_LEN 256
+#define MAX_DNS_QUESTIONS 8
+
+struct dns_question {
+ char name[MAX_DNS_NAME_LEN];
+ uint16_t qtype;
+};
+
+const char *dns_type_to_string(uint16_t type);
+size_t parse_dns_udp(const uint8_t *payload, size_t len, struct dns_question *out, size_t max_qs);
diff --git a/include/entropy.h b/include/entropy.h
new file mode 100644
index 0000000..e246682
--- /dev/null
+++ b/include/entropy.h
@@ -0,0 +1,12 @@
+#include <stddef.h>
+
+#pragma once
+
+typedef struct entropy_ctx {
+ size_t freq[256];
+ size_t total_bytes;
+} entropy_ctx;
+
+void entropy_init(entropy_ctx *ctx);
+void entropy_update(entropy_ctx *ctx, const unsigned char *buf, size_t len);
+double entropy_final(entropy_ctx *ctx);
diff --git a/include/error.h b/include/error.h
new file mode 100644
index 0000000..5ae9b0d
--- /dev/null
+++ b/include/error.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <string.h>
+#include <errno.h>
+
+
+void error(const char *, ...);
+void error_fatal(const char *, ...);
diff --git a/include/fanotify.h b/include/fanotify.h
new file mode 100644
index 0000000..d58109f
--- /dev/null
+++ b/include/fanotify.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "agent_context.h"
+
+int setup_fanotify(void);
+void select_fanotify(int fan_fd, agent_context_t *ctx);
diff --git a/include/hash_ledger.h b/include/hash_ledger.h
new file mode 100644
index 0000000..24e2ac3
--- /dev/null
+++ b/include/hash_ledger.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#define MAX_MATCHED_PATTERNS 32
+#define MAX_MATCHED_RULES 16
+
+typedef enum {
+ VERDICT_UNKNOWN = 0,
+ VERDICT_ALLOW,
+ VERDICT_BLOCK,
+ VERDICT_QUARANTINE,
+ VERDICT_INFORMATIONAL
+} scan_verdict_t;
+
+struct hash_entry {
+ char path[PATH_MAX];
+ char md5[33];
+ char sha256[65];
+ double entropy;
+ struct stat sb;
+ time_t last_scanned;
+ size_t scan_count;
+
+ scan_verdict_t verdict;
+ const char *matched_patterns[MAX_MATCHED_PATTERNS];
+ size_t matched_pattern_count;
+ const char *matched_rules[MAX_MATCHED_RULES];
+ size_t matched_rule_count;
+
+ struct hash_entry *next; // collision handling
+};
+
+struct hash_ledger {
+ size_t num_buckets;
+ struct hash_entry **buckets;
+};
+
+// structure to hold output for multihash()
+struct multihash {
+ char md5[33];
+ char sha256[65];
+ double entropy;
+};
+
+// TODO hash_ledger_t
+struct hash_ledger *hash_ledger_init(size_t num_buckets);
+void hash_ledger_destroy(struct hash_ledger *ledger);
+size_t hash_ledger_bucket(struct hash_ledger *ledger, const char *path);
+struct hash_entry *hash_ledger_find(struct hash_ledger *ledger, const char *path);
+struct hash_entry *hash_ledger_add_or_update(struct hash_ledger *ledger,
+ const char *path,
+ struct stat *sb);
+bool multihash_file(const char *path, struct multihash *out);
diff --git a/include/json.h b/include/json.h
new file mode 100644
index 0000000..db508df
--- /dev/null
+++ b/include/json.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define JSON_MAX_DEPTH 8
+
+typedef enum {
+ JSON_CONTEXT_OBJECT,
+ JSON_CONTEXT_ARRAY
+} json_context_t;
+
+typedef struct {
+ char *data;
+ size_t length;
+ size_t capacity;
+ json_context_t context_stack[JSON_MAX_DEPTH];
+ int context_depth;
+ bool needs_comma[JSON_MAX_DEPTH];
+} json_t;
+
+void json_init(json_t *buf);
+void json_free(json_t *buf);
+const char *json_get(json_t *buf);
+
+void json_start_object(json_t *buf);
+void json_end_object(json_t *buf);
+void json_set_needs_comma(json_t *buf);
+bool json_needs_comma(json_t *buf);
+
+void json_append(json_t *buf, const char *fmt, ...);
+void json_escape_string(json_t *buf, const char *input);
+
+void json_add_string(json_t *buf, const char *key, const char *value);
+void json_add_string_or_null(json_t *buf, const char *key, const char *value);
+void json_add_int(json_t *buf, const char *key, int value);
+void json_add_int64(json_t *buf, const char *key, int64_t value);
+void json_add_uint64(json_t *buf, const char *key, uint64_t value);
+void json_add_double(json_t *buf, const char *key, double value);
+void json_add_bool(json_t *buf, const char *key, bool value);
+void json_add_null(json_t *buf, const char *key);
+
+void json_start_array(json_t *buf);
+void json_end_array(json_t *buf);
+void json_add_array_start(json_t *buf, const char *key);
+
+void json_array_add_string(json_t *buf, const char *value);
+void json_array_add_int(json_t *buf, int value);
+void json_array_add_int64(json_t *buf, int64_t value);
+void json_array_add_uint64(json_t *buf, uint64_t value);
+void json_array_add_double(json_t *buf, double value);
+void json_array_add_bool(json_t *buf, bool value);
+void json_array_add_null(json_t *buf);
diff --git a/include/md5.h b/include/md5.h
new file mode 100644
index 0000000..dedb0d1
--- /dev/null
+++ b/include/md5.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#define MD5_DIGEST_LENGTH 16
+#define MD5_TOO_LARGE "TOOLARGETOHASH"
+
+char *md5_digest_file(const char *);
diff --git a/include/net.h b/include/net.h
new file mode 100644
index 0000000..83e1c47
--- /dev/null
+++ b/include/net.h
@@ -0,0 +1,9 @@
+#pragma once
+
+typedef int sock_t;
+typedef unsigned short port_t;
+
+int sockprintf(int, const char *, ...);
+bool validate_ip(const char *);
+bool validate_ipv4(const char *);
+char *get_default_iface(void);
diff --git a/include/output.h b/include/output.h
new file mode 100644
index 0000000..cb59956
--- /dev/null
+++ b/include/output.h
@@ -0,0 +1,4 @@
+#pragma once
+
+void output(const char *);
+void msg(const char *, ...);
diff --git a/include/proc.h b/include/proc.h
new file mode 100644
index 0000000..be737a6
--- /dev/null
+++ b/include/proc.h
@@ -0,0 +1,49 @@
+#pragma once
+
+// /proc/PID/status
+struct proc_status {
+ char name[17];
+ char state;
+ pid_t pid;
+ pid_t tgid;
+ pid_t ppid;
+ pid_t tracer_pid;
+ uid_t uid;
+ uid_t euid;
+ uid_t ssuid;
+ uid_t fsuid;
+ gid_t gid;
+ gid_t egid;
+ gid_t ssgid;
+ gid_t fsgid;
+ unsigned int fdsize;
+ unsigned int threads;
+ unsigned long cap_inh;
+ unsigned long cap_prm;
+ unsigned long cap_eff;
+ unsigned long cap_bnd;
+ unsigned long cap_amb;
+ int no_new_privs;
+ int seccomp;
+};
+
+// /proc/PID/stat
+struct proc_stat {
+ char state;
+ unsigned long starttime;
+ unsigned long utime; // user time
+ unsigned long stime; // kernel time
+ long priority;
+ long nice;
+ unsigned long vsize;
+ long rss;
+ int tty_nr;
+};
+
+char *proc_cwd(pid_t);
+char *proc_environ(pid_t);
+char *proc_exe_path(pid_t);
+char *proc_get_exe_path(pid_t);
+char *proc_get_cmdline(pid_t);
+struct proc_stat proc_parse_stat(pid_t);
+struct proc_status proc_get_status(pid_t);
diff --git a/include/proc_connector.h b/include/proc_connector.h
new file mode 100644
index 0000000..10a20ff
--- /dev/null
+++ b/include/proc_connector.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <linux/connector.h>
+#include <linux/cn_proc.h>
+
+#include "agent_context.h"
+#include "proc_ledger.h"
+#include "json.h"
+#include "net.h"
+
+sock_t setup_proc_connector(void);
+void select_proc_connector(sock_t, agent_context_t *);
+
+json_t handle_PROC_EVENT_SID(struct proc_event *, agent_context_t *);
+json_t handle_PROC_EVENT_COMM(struct proc_event *, agent_context_t *, const char *);
+json_t handle_PROC_EVENT_COREDUMP(struct proc_event *, agent_context_t *);
+json_t handle_PROC_EVENT_FORK(struct proc_event *, agent_context_t *);
+json_t handle_PROC_EVENT_EXEC(struct proc_event *, agent_context_t *);
+json_t handle_PROC_EVENT_EXEC_environment(struct proc_event *, agent_context_t *);
+json_t handle_PROC_EVENT_EXIT(struct proc_event *, agent_context_t *);
+json_t handle_PROC_EVENT_UID(struct proc_event *, agent_context_t *, int, int);
+json_t handle_PROC_EVENT_GID(struct proc_event *, agent_context_t *, int, int);
+json_t handle_PROC_EVENT_PTRACE(struct proc_event *, agent_context_t *);
+json_t handle_PROC_EVENT_UNKNOWN(struct proc_event *, agent_context_t *);
diff --git a/include/proc_ledger.h b/include/proc_ledger.h
new file mode 100644
index 0000000..d715384
--- /dev/null
+++ b/include/proc_ledger.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <time.h>
+#include <limits.h>
+//#include <pthread.h> // for the future..maybe...
+
+#include "json.h"
+
+struct agent_context;
+typedef struct agent_context agent_context_t;
+
+struct proc_ledger_entry {
+ pid_t pid;
+ pid_t tgid;
+ pid_t ppid;
+ char exe[PATH_MAX];
+ char comm[17];
+ char cmdline[4096];
+ char cwd[PATH_MAX];
+ uid_t uid;
+ uid_t euid;
+ gid_t gid;
+ gid_t egid;
+ time_t start_time;
+ unsigned long cpu_user_ticks;
+ unsigned long cpu_kernel_ticks;
+ long rss;
+ unsigned long vsize;
+ bool daemonized;
+ bool is_traced;
+ pid_t tracer_pid;
+ char state;
+ int seccomp;
+ unsigned long cap_eff;
+ unsigned int threads;
+ bool has_tty;
+ struct proc_ledger_entry *next; // for hash collisions
+};
+
+struct proc_ledger {
+ size_t num_buckets;
+ struct proc_ledger_entry **buckets;
+ //pthread_mutex_t lock; // for the future..maybe...
+};
+
+struct proc_ledger *proc_ledger_init(size_t num_buckets);
+void proc_ledger_destroy(struct proc_ledger *ledger);
+
+struct proc_ledger_entry *proc_ledger_find(struct proc_ledger *ledger, pid_t pid);
+struct proc_ledger_entry *proc_ledger_entry_create(pid_t pid, agent_context_t *ctx);
+bool proc_ledger_add(struct proc_ledger *ledger, struct proc_ledger_entry *entry);
+bool proc_ledger_remove(struct proc_ledger *ledger, pid_t pid);
+bool proc_ledger_replace(struct proc_ledger *ledger, struct proc_ledger_entry *new_entry);
+json_t proc_ledger_entry_to_json(struct proc_ledger_entry *entry,
+ const char *event_type,
+ struct agent_context *ctx);
+void proc_ledger_hydrate(agent_context_t *ctx);
+size_t proc_ledger_bucket(struct proc_ledger *ledger, pid_t pid);
diff --git a/include/sha256.h b/include/sha256.h
new file mode 100644
index 0000000..72cf9c3
--- /dev/null
+++ b/include/sha256.h
@@ -0,0 +1,40 @@
+/*********************************************************************
+* Filename: sha256.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding SHA256 implementation.
+*********************************************************************/
+
+#ifndef SHA256_H
+#define SHA256_H
+
+/*************************** HEADER FILES ***************************/
+#include <stddef.h>
+#include <stdint.h>
+
+/****************************** MACROS ******************************/
+#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
+#define SHA256_DIGEST_LENGTH SHA256_BLOCK_SIZE
+#define SHA256_TOO_LARGE "TOOLARGETOHASH"
+
+#undef SHA256_SHOW_ERRORS // for debugging
+
+/**************************** DATA TYPES ****************************/
+typedef unsigned char BYTE; // 8-bit byte
+typedef uint32_t WORD; // 32-bit word
+
+typedef struct {
+ BYTE data[64];
+ WORD datalen;
+ uint64_t bitlen;
+ //unsigned long long bitlen;
+ WORD state[8];
+} SHA256_CTX;
+
+/*********************** FUNCTION DECLARATIONS **********************/
+void sha256_init(SHA256_CTX *);
+void sha256_update(SHA256_CTX *, const BYTE [], size_t);
+void sha256_final(SHA256_CTX *, BYTE []);
+char *sha256_digest_file(const char *);
+#endif // SHA256_H
diff --git a/include/sniffer.h b/include/sniffer.h
new file mode 100644
index 0000000..8b85100
--- /dev/null
+++ b/include/sniffer.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "agent_context.h"
+#include "net.h"
+
+#ifndef TH_FIN
+#define TH_FIN 0x01
+#endif
+
+#ifndef TH_SYN
+#define TH_SYN 0x02
+#endif
+
+#ifndef TH_RST
+#define TH_RST 0x04
+#endif
+
+#ifndef TH_PUSH
+#define TH_PUSH 0x08
+#endif
+
+#ifndef TH_ACK
+#define TH_ACK 0x10
+#endif
+
+#ifndef TH_URG
+#define TH_URG 0x20
+#endif
+
+#ifndef TH_ECE
+#define TH_ECE 0x40
+#endif
+
+#ifndef TH_CWR
+#define TH_CWR 0x80
+#endif
+
+sock_t sniffer_init_interface(const char *interface, bool promisc);
+void sniffer_handle_packet(sock_t sniffer, agent_context_t *ctx);
+
+pid_t match_udp_inode(uint32_t ip_be, uint16_t port_be);
diff --git a/include/string_common.h b/include/string_common.h
new file mode 100644
index 0000000..bca8985
--- /dev/null
+++ b/include/string_common.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <stdbool.h>
+
+bool startswith(const char *, const char *);
+bool endswith(const char *, const char *);
diff --git a/include/time_common.h b/include/time_common.h
new file mode 100644
index 0000000..9f39eef
--- /dev/null
+++ b/include/time_common.h
@@ -0,0 +1,4 @@
+#pragma once
+
+double timestamp(void);
+unsigned long get_boot_time(void);