Something went wrong. Try again.
Adversarial C2 Protocol Implemented in Zig
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204// Copyright 2026 Robby Zambito//// This file is part of zaprus.//// Zaprus is free software: you can redistribute it and/or modify it under the// terms of the GNU General Public License as published by the Free Software// Foundation, either version 3 of the License, or (at your option) any later// version.//// Zaprus is distributed in the hope that it will be useful, but WITHOUT ANY// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR// A PARTICULAR PURPOSE. See the GNU General Public License for more details.//// You should have received a copy of the GNU General Public License along with// Zaprus. If not, see <https://www.gnu.org/licenses/>.
const RawSocket = @This();
const is_debug = builtin.mode == .Debug;
fd: i32,sockaddr_ll: std.posix.sockaddr.ll,mac: [6]u8,
pub const max_payload_len = 1000;
const Ifconf = extern struct { ifc_len: i32, ifc_ifcu: extern union { ifcu_buf: ?[*]u8, ifcu_req: ?[*]std.os.linux.ifreq, },};
pub fn init() error{ SocketError, NicError, NoInterfaceFound, BindError,}!RawSocket { const socket: i32 = std.math.cast(i32, std.os.linux.socket(std.os.linux.AF.PACKET, std.os.linux.SOCK.RAW, 0)) orelse return error.SocketError; if (socket < 0) return error.SocketError;
var ifreq_storage: [16]std.os.linux.ifreq = undefined; var ifc = Ifconf{ .ifc_len = @sizeOf(@TypeOf(ifreq_storage)), .ifc_ifcu = .{ .ifcu_req = &ifreq_storage }, };
if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFCONF, @intFromPtr(&ifc)) != 0) { return error.NicError; }
const count = @divExact(ifc.ifc_len, @sizeOf(std.os.linux.ifreq));
// Get the first non loopback interface var ifr = for (ifreq_storage[0..@intCast(count)]) |*ifr| { if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFFLAGS, @intFromPtr(ifr)) == 0) { if (ifr.ifru.flags.LOOPBACK) continue; break ifr; } } else return error.NoInterfaceFound;
// 2. Get Interface Index if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFINDEX, @intFromPtr(ifr)) != 0) { return error.NicError; } const ifindex: i32 = ifr.ifru.ivalue;
// 3. Get Real MAC Address if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFHWADDR, @intFromPtr(ifr)) != 0) { return error.NicError; } var mac: [6]u8 = ifr.ifru.hwaddr.data[0..6].*; if (builtin.cpu.arch.endian() == .little) std.mem.reverse(u8, &mac);
// 4. Set Flags (Promiscuous/Broadcast) if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFFLAGS, @intFromPtr(ifr)) != 0) { return error.NicError; } ifr.ifru.flags.BROADCAST = true; ifr.ifru.flags.PROMISC = true; if (std.os.linux.ioctl(socket, std.os.linux.SIOCSIFFLAGS, @intFromPtr(ifr)) != 0) { return error.NicError; }
const sockaddr_ll = std.mem.zeroInit(std.posix.sockaddr.ll, .{ .family = std.posix.AF.PACKET, .ifindex = ifindex, .protocol = std.mem.nativeToBig(u16, @as(u16, std.os.linux.ETH.P.IP)), });
const bind_ret = std.os.linux.bind(socket, @ptrCast(&sockaddr_ll), @sizeOf(@TypeOf(sockaddr_ll))); if (bind_ret != 0) return error.BindError;
return .{ .fd = socket, .sockaddr_ll = sockaddr_ll, .mac = mac, };}
pub fn setTimeout(self: *RawSocket, sec: isize, usec: i64) error{SetTimeoutError}!void { const timeout: std.os.linux.timeval = .{ .sec = sec, .usec = usec }; const timeout_ret = std.os.linux.setsockopt(self.fd, std.os.linux.SOL.SOCKET, std.os.linux.SO.RCVTIMEO, @ptrCast(&timeout), @sizeOf(@TypeOf(timeout))); if (timeout_ret != 0) return error.SetTimeoutError;}
pub fn deinit(self: *RawSocket) void { _ = std.os.linux.close(self.fd); self.* = undefined;}
pub fn send(self: RawSocket, payload: []const u8) !void { const sent_bytes = std.os.linux.sendto( self.fd, payload.ptr, payload.len, 0, @ptrCast(&self.sockaddr_ll), @sizeOf(@TypeOf(self.sockaddr_ll)), ); _ = sent_bytes;}
pub fn receive(self: RawSocket, buf: []u8) ![]u8 { const len = std.os.linux.recvfrom( self.fd, buf.ptr, buf.len, 0, null, null, ); if (std.os.linux.errno(len) != .SUCCESS) { return error.Timeout; // TODO: get the real error, assume timeout for now. } return buf[0..len];}
pub fn attachSaprusPortFilter(self: RawSocket, incoming_src_port: ?u16, incoming_dest_port: u16) !void { const BPF = std.os.linux.BPF; // BPF instruction structure for classic BPF const SockFilter = extern struct { code: u16, jt: u8, jf: u8, k: u32, };
const SockFprog = extern struct { len: u16, filter: [*]const SockFilter, };
// Build the filter program const filter = if (incoming_src_port) |inc_src| &[_]SockFilter{ // Load 2 bytes at offset 46 (absolute) .{ .code = BPF.LD | BPF.H | BPF.ABS, .jt = 0, .jf = 0, .k = 46 }, // Jump if equal to port (skip 1 if true, skip 0 if false) .{ .code = BPF.JMP | BPF.JEQ | BPF.K, .jt = 1, .jf = 0, .k = @as(u32, inc_src) }, // Return 0x0 (fail) .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0x0 }, // Load 2 bytes at offset 48 (absolute) .{ .code = BPF.LD | BPF.H | BPF.ABS, .jt = 0, .jf = 0, .k = 48 }, // Jump if equal to port (skip 0 if true, skip 1 if false) .{ .code = BPF.JMP | BPF.JEQ | BPF.K, .jt = 0, .jf = 1, .k = @as(u32, incoming_dest_port) }, // Return 0xffff (pass) .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0xffff }, // Return 0x0 (fail) .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0x0 }, } else &[_]SockFilter{ // Load 2 bytes at offset 48 (absolute) .{ .code = BPF.LD | BPF.H | BPF.ABS, .jt = 0, .jf = 0, .k = 48 }, // Jump if equal to port (skip 0 if true, skip 1 if false) .{ .code = BPF.JMP | BPF.JEQ | BPF.K, .jt = 0, .jf = 1, .k = @as(u32, incoming_dest_port) }, // Return 0xffff (pass) .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0xffff }, // Return 0x0 (fail) .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0x0 }, };
const fprog = SockFprog{ .len = @intCast(filter.len), .filter = filter.ptr, };
// Attach filter to socket using setsockopt const rc = std.os.linux.setsockopt( self.fd, std.os.linux.SOL.SOCKET, std.os.linux.SO.ATTACH_FILTER, @ptrCast(&fprog), @sizeOf(SockFprog), );
if (rc != 0) { return error.BpfAttachFailed; }}
const std = @import("std");const builtin = @import("builtin");