Adversarial C2 Protocol Implemented in Zig
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

it works well!

+108 -152
+96 -140
src/Client.zig
··· 30 30 31 31 /// Used for relay messages and connection handshake. 32 32 /// Assumes Client .init has been called. 33 - fn broadcastInitialInterestMessage(self: *Self, msg_bytes: []align(@alignOf(SaprusMessage)) u8) !void { 33 + fn broadcastInitialInterestMessage(self: *Self, msg_bytes: []u8) !void { 34 34 const writer = self.writer; 35 35 36 - // const EthernetHeaders = packed struct { 37 - // dest_mac: @Vector(6, u8), 38 - 39 - // src_mac: @Vector(6, u8), 40 - 41 - // ether_type: u16, 42 - // }; 43 - 44 - const IpHeaders = packed struct { 45 - _: u8 = 0x45, 46 - // ip_version: u4, 47 - // header_length: u4 = 0, 48 - type_of_service: u8 = 0, 49 - total_length: u16 = 0x04, 50 - 51 - identification: u16 = 0, 52 - __: u16 = 0x0, 53 - // ethernet_flags: u3 = 0, 54 - // fragment_offset: u13 = 0, 55 - ttl: u8 = 0, 56 - protocol: u8 = 0, 57 - 58 - header_checksum: @Vector(2, u8) = .{ 0, 0 }, 59 - src_ip: @Vector(4, u8), 60 - 61 - dest_ip: @Vector(4, u8), 62 - }; 63 - 64 - const UdpHeaders = packed struct { 65 - src_port: @Vector(2, u8), 66 - 67 - dest_port: @Vector(2, u8), 68 - length: u16, 69 - checksum: @Vector(2, u8) = .{ 0, 0 }, 70 - }; 71 - 72 - // const total_len = ((@bitSizeOf(IpHeaders) + @bitSizeOf(UdpHeaders)) / 8) + msg_bytes.len; 73 - const total_len = 130; 36 + const total_len = ((@bitSizeOf(EthernetHeaders) + @bitSizeOf(IpHeaders) + @bitSizeOf(UdpHeaders)) / 8) + msg_bytes.len; 74 37 std.debug.assert(writer.buffer.len >= total_len); 75 38 _ = writer.consumeAll(); 76 39 77 - // var ether_headers: EthernetHeaders = .{ 78 - // .dest_mac = .{ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff }, 79 - // // .src_mac = .{ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }, 80 - // .src_mac = blk: { 81 - // var output_bytes: [6]u8 = undefined; 82 - // // const r_bytes = try writer.writableArray(6); 83 - // self.rand.bytes(&output_bytes); 84 - // break :blk output_bytes; 85 - // }, 86 - // .ether_type = 0x0800, 87 - // }; 40 + const ether_headers: EthernetHeaders = .{ 41 + .dest_mac = .{ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff }, 42 + .src_mac = .{ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }, 43 + // .src_mac = blk: { 44 + // var output_bytes: [6]u8 = undefined; 45 + // // const r_bytes = try writer.writableArray(6); 46 + // self.rand.bytes(&output_bytes); 47 + // break :blk output_bytes; 48 + // }, 49 + .ether_type = 0x0800, 50 + }; 88 51 89 - var ip_headers: IpHeaders = .{ 52 + // @compileLog((@bitSizeOf(EthernetHeaders) / 8)); 53 + const ip_headers: IpHeaders = .{ 90 54 // .ip_version = 0x4, 91 55 // .header_length = 0x5, 92 - // .total_length = 130, //@intCast(total_len - 8), // 8 is the ethernet frame length (macs + type) 93 - .total_length = 0x00, 56 + .total_length = @intCast(total_len - 92), 94 57 .ttl = 0x64, 95 58 .protocol = 0x11, 96 59 .src_ip = .{ 0xff, 0x02, 0x03, 0x04 }, 97 60 .dest_ip = .{ 0xff, 0xff, 0xff, 0xff }, 98 61 }; 99 62 100 - var udp_headers: UdpHeaders = .{ 101 - .src_port = .{ 0, 0x01 }, 102 - .dest_port = .{ 0xb8, 0x22 }, 63 + const udp_headers: UdpHeaders = .{ 64 + .src_port = 0xbbbb, 65 + .dest_port = 8888, 103 66 .length = @intCast(msg_bytes.len), 104 67 }; 105 - _ = &ip_headers; 106 - _ = &udp_headers; 107 - // _ = &ether_headers; 108 - 109 - // _ = try writer.write(&@as([@bitSizeOf(UdpHeaders) / 8]u8, @bitCast(headers))); 110 - 111 - // std.mem.byteSwapAllFields(EthernetHeaders, &ether_headers); 112 - // try writer.writeStruct(ether_headers, native_endian); 113 - 114 - std.mem.byteSwapAllFields(IpHeaders, &ip_headers); 115 - try writer.writeStruct(ip_headers, native_endian); 116 - 117 - // std.mem.byteSwapAllFields(UdpHeaders, &udp_headers); 118 - // try writer.writeStruct(udp_headers, native_endian); 119 68 120 - // // Ensure buffer is large enough 121 - // std.debug.assert(writer.buffer.len > 38 + msg_bytes.len); 122 - 123 - // // Ensure writer is clean 124 - // writer.consumeAll(); 125 - 126 - // // Destination MAC addr to FF:FF:FF:FF:FF:FF 127 - // try writer.write(); 128 - 129 - // // Source MAC to random bytes 130 - // { 131 - // } 132 - 133 - // // 96 bits 134 - 135 - // // Set EtherType to IPv4 136 - // try writer.write(.{ 0x08, 0x00 }); 137 - 138 - // // 112 bits 139 - 140 - // // Set IPv4 version to 4 141 - // try writer.writeByte(0x45); 142 - 143 - // // 120 bits 144 - 145 - // // Unset section (Version, IHL, ToS) 146 - // writer.advance(2); 147 - 148 - // // 136 bits 149 - 150 - // // Total length 151 - // try write.writeInt(u16, 38 + msg_bytes.len); 152 - 153 - // // Destination broadcast 154 - // writer.splatByte(0xff, 0x22 - 0x1e); 155 - 156 - // var packet_bytes: [_]u8 = comptime blk: { 157 - // var b: [max_message_size]u8 = @splat(0); 158 - 159 - // for (0x1e..0x22) |i| { 160 - // b[i] = 0xff; 161 - // } 162 - 163 - // // Set TTL 164 - // b[0x16] = 0x40; 69 + try ether_headers.write(writer); 70 + try ip_headers.write(writer); 71 + try udp_headers.write(writer); 165 72 166 - // // Set IPv4 protocol to UDP 167 - // b[0x17] = 0x11; 73 + // Saprus 74 + const msg_target_bytes = try writer.writableSlice(msg_bytes.len); 75 + @memcpy(msg_target_bytes, msg_bytes); 76 + var msg_target: *align(1) SaprusMessage = try .bytesAsValue(msg_target_bytes); 77 + try msg_target.networkFromNativeEndian(); 168 78 169 - // // Set interest filter value to 8888. 170 - // b[0x24] = 0x22; 171 - // b[0x25] = 0xb8; 172 - // break :blk &b; 173 - // }; 174 - var msg: *SaprusMessage = try .bytesAsValue(msg_bytes); 175 - try msg.networkFromNativeEndian(); 176 - defer msg.nativeFromNetworkEndian() catch unreachable; 177 - 178 - // std.debug.print("headers: {x}\n", .{@as([@bitSizeOf(UdpHeaders) / 8]u8, @bitCast(headers))}); 179 79 std.debug.print("bytes: {x}\n", .{writer.buffer[0..writer.end]}); 180 - _ = try writer.write(msg_bytes); 181 - 182 - // The byte position within the packet that the saprus message starts at. 183 - // const saprus_start_byte = 42; 184 - // @memcpy(packet_bytes[saprus_start_byte .. saprus_start_byte + msg_bytes.len], msg_bytes); 185 - // _ = try writer.write(packet_bytes[0 .. saprus_start_byte + msg_bytes.len]); 186 80 try writer.flush(); 187 81 } 188 82 189 83 // fn broadcastSaprusMessage(msg_bytes: []align(@alignOf(SaprusMessage)) u8) !void {} 190 84 191 - fn broadcastSaprusMessage(msg_bytes: []align(@alignOf(SaprusMessage)) u8, udp_port: u16) !void { 192 - const msg: *SaprusMessage = try .bytesAsValue(msg_bytes); 85 + fn broadcastSaprusMessage(msg_bytes: []u8, udp_port: u16) !void { 86 + const msg: *align(1) SaprusMessage = try .bytesAsValue(msg_bytes); 193 87 try msg.networkFromNativeEndian(); 194 88 defer msg.nativeFromNetworkEndian() catch unreachable; 195 89 ··· 222 116 .relay, 223 117 base64Enc.calcSize(payload.len), 224 118 )]; 225 - const msg: *SaprusMessage = .init(.relay, msg_bytes); 119 + const msg: *align(1) SaprusMessage = .init(.relay, msg_bytes); 226 120 227 121 const relay = (try msg.getSaprusTypePayload()).relay; 228 122 relay.dest = dest; ··· 238 132 pub fn sendInitialConnection( 239 133 self: Self, 240 134 payload: []const u8, 241 - output_bytes: []align(@alignOf(SaprusMessage)) u8, 135 + output_bytes: []u8, 242 136 initial_port: u16, 243 - ) !*SaprusMessage { 137 + ) !*align(1) SaprusMessage { 244 138 const dest_port = self.randomPort(); 245 139 const msg_bytes = output_bytes[0..try SaprusMessage.calcSize( 246 140 .connection, 247 141 base64Enc.calcSize(payload.len), 248 142 )]; 249 - const msg: *SaprusMessage = .init(.connection, msg_bytes); 143 + const msg: *align(1) SaprusMessage = .init(.connection, msg_bytes); 250 144 251 145 const connection = (try msg.getSaprusTypePayload()).connection; 252 146 connection.src_port = initial_port; ··· 261 155 pub fn connect(self: Self, payload: []const u8) !?SaprusConnection { 262 156 const initial_port = self.randomPort(); 263 157 264 - var initial_conn_res: ?*SaprusMessage = null; 158 + var initial_conn_res: ?*align(1) SaprusMessage = null; 265 159 266 160 var sock = try network.Socket.create(.ipv4, .udp); 267 161 defer sock.close(); ··· 293 187 } 294 188 return null; 295 189 } 190 + 191 + const EthernetHeaders = struct { 192 + dest_mac: @Vector(6, u8), 193 + 194 + src_mac: @Vector(6, u8), 195 + 196 + ether_type: u16, 197 + 198 + fn write(hdr: @This(), writer: *std.Io.Writer) !void { 199 + try writer.writeInt(u48, @bitCast(hdr.dest_mac), .big); 200 + try writer.writeInt(u48, @bitCast(hdr.src_mac), .big); 201 + try writer.writeInt(u16, hdr.ether_type, .big); 202 + } 203 + }; 204 + 205 + const IpHeaders = struct { 206 + _: u8 = 0x45, 207 + // ip_version: u4, 208 + // header_length: u4 = 0, 209 + type_of_service: u8 = 0, 210 + total_length: u16 = 0x04, 211 + 212 + identification: u16 = 0, 213 + __: u16 = 0x0, 214 + // ethernet_flags: u3 = 0, 215 + // fragment_offset: u13 = 0, 216 + ttl: u8 = 0, 217 + protocol: u8 = 0, 218 + 219 + header_checksum: @Vector(2, u8) = .{ 0, 0 }, 220 + src_ip: @Vector(4, u8), 221 + 222 + dest_ip: @Vector(4, u8), 223 + 224 + fn write(hdr: @This(), writer: *std.Io.Writer) !void { 225 + try writer.writeInt(u8, 0x45, .big); // ip version and header length 226 + try writer.writeByte(hdr.type_of_service); 227 + try writer.writeInt(u16, hdr.total_length, .big); 228 + try writer.writeInt(u16, hdr.identification, .big); 229 + try writer.writeInt(u16, 0x00, .big); // ethernet flags and fragment offset 230 + try writer.writeByte(hdr.ttl); 231 + try writer.writeByte(hdr.protocol); 232 + try writer.writeInt(u16, @bitCast(hdr.header_checksum), .big); 233 + try writer.writeInt(u32, @bitCast(hdr.src_ip), .big); 234 + try writer.writeInt(u32, @bitCast(hdr.dest_ip), .big); 235 + } 236 + }; 237 + 238 + const UdpHeaders = packed struct { 239 + src_port: u16, 240 + 241 + dest_port: u16, 242 + length: u16, 243 + checksum: @Vector(2, u8) = .{ 0, 0 }, 244 + 245 + fn write(hdr: @This(), writer: *std.Io.Writer) !void { 246 + try writer.writeInt(u16, hdr.src_port, .big); 247 + try writer.writeInt(u16, hdr.dest_port, .big); 248 + try writer.writeInt(u16, hdr.length, .big); 249 + try writer.writeInt(u16, @bitCast(hdr.checksum), .big); 250 + } 251 + }; 296 252 297 253 const SaprusMessage = @import("message.zig").Message; 298 254 const SaprusConnection = @import("Connection.zig");
+12 -12
src/message.zig
··· 71 71 }; 72 72 73 73 const Self = @This(); 74 - const SelfBytes = []align(@alignOf(Self)) u8; 74 + const SelfBytes = []align(1) u8; 75 75 76 76 type: PacketType, 77 77 length: u16, ··· 81 81 /// This properly initializes the top level headers within the slice. 82 82 /// This is used for creating new messages. For reading messages from the network, 83 83 /// see: networkBytesAsValue. 84 - pub fn init(@"type": PacketType, bytes: []align(@alignOf(Self)) u8) *Self { 84 + pub fn init(@"type": PacketType, bytes: []u8) *align(1) Self { 85 85 std.debug.assert(bytes.len >= @sizeOf(Self)); 86 - const res: *Self = @ptrCast(bytes.ptr); 86 + const res: *align(1) Self = @ptrCast(bytes.ptr); 87 87 res.type = @"type"; 88 88 res.length = @intCast(bytes.len - @sizeOf(Self)); 89 89 return res; ··· 100 100 return @intCast(payload_len + @sizeOf(Self) + header_size); 101 101 } 102 102 103 - fn getRelay(self: *Self) *align(1) Relay { 103 + fn getRelay(self: *align(1) Self) *align(1) Relay { 104 104 return std.mem.bytesAsValue(Relay, &self.bytes); 105 105 } 106 - fn getConnection(self: *Self) *align(1) Connection { 106 + fn getConnection(self: *align(1) Self) *align(1) Connection { 107 107 return std.mem.bytesAsValue(Connection, &self.bytes); 108 108 } 109 109 110 110 /// Access the message Saprus payload. 111 - pub fn getSaprusTypePayload(self: *Self) MessageTypeError!(union(PacketType) { 111 + pub fn getSaprusTypePayload(self: *align(1) Self) MessageTypeError!(union(PacketType) { 112 112 relay: *align(1) Relay, 113 113 file_transfer: void, 114 114 connection: *align(1) Connection, ··· 122 122 } 123 123 124 124 /// Convert the message to native endianness from network endianness in-place. 125 - pub fn nativeFromNetworkEndian(self: *Self) MessageTypeError!void { 125 + pub fn nativeFromNetworkEndian(self: *align(1) Self) MessageTypeError!void { 126 126 self.type = @enumFromInt(bigToNative( 127 127 @typeInfo(@TypeOf(self.type)).@"enum".tag_type, 128 128 @intFromEnum(self.type), ··· 146 146 } 147 147 148 148 /// Convert the message to network endianness from native endianness in-place. 149 - pub fn networkFromNativeEndian(self: *Self) MessageTypeError!void { 149 + pub fn networkFromNativeEndian(self: *align(1) Self) MessageTypeError!void { 150 150 try switch (try self.getSaprusTypePayload()) { 151 151 .relay => {}, 152 152 .connection => |*con| con.*.networkFromNativeEndian(), ··· 161 161 } 162 162 163 163 /// Convert network endian bytes to a native endian value in-place. 164 - pub fn networkBytesAsValue(bytes: SelfBytes) MessageParseError!*Self { 164 + pub fn networkBytesAsValue(bytes: SelfBytes) MessageParseError!*align(1) Self { 165 165 const res = std.mem.bytesAsValue(Self, bytes); 166 166 try res.nativeFromNetworkEndian(); 167 167 return .bytesAsValue(bytes); ··· 169 169 170 170 /// Create a structured view of the bytes without initializing the length or type, 171 171 /// and without converting the endianness. 172 - pub fn bytesAsValue(bytes: SelfBytes) MessageParseError!*Self { 172 + pub fn bytesAsValue(bytes: SelfBytes) MessageParseError!*align(1) Self { 173 173 const res = std.mem.bytesAsValue(Self, bytes); 174 174 return switch (res.type) { 175 175 .relay, .connection => if (bytes.len == res.length + @sizeOf(Self)) ··· 183 183 184 184 /// Deprecated. 185 185 /// If I need the bytes, I should just pass around the slice that is backing this to begin with. 186 - pub fn asBytes(self: *Self) SelfBytes { 186 + pub fn asBytes(self: *align(1) Self) SelfBytes { 187 187 const size = @sizeOf(Self) + self.length; 188 - return @as([*]align(@alignOf(Self)) u8, @ptrCast(self))[0..size]; 188 + return @as([*]align(1) u8, @ptrCast(self))[0..size]; 189 189 } 190 190 }; 191 191