简介:木马是一种恶意软件,通过伪装成合法程序来欺骗用户安装。本课程专注于使用C语言进行socket编程,构建基础但实用的木马程序。我们将涵盖C语言基础、socket通信、系统调用、文件I/O操作及安全编程等关键技术点,以帮助学生深入理解木马机制及其防御策略。
1. C语言编程基础
1.1 C语言概述
C语言是一种广泛使用的计算机编程语言,以其高效、灵活和可移植性著称。它常用于系统软件、操作系统、嵌入式系统以及许多其他类型的软件开发。C语言的这种普及性使得它成为IT行业从业者必须掌握的基本技能之一。
1.2 数据类型与变量
C语言的基本数据类型包括整型、浮点型、字符型等。变量是数据的存储位置,需要在使用前声明其类型。例如,声明一个整型变量可以使用 int a; 。
1.3 控制结构
控制结构是编程中实现逻辑流程的关键,包括条件判断和循环控制。条件判断通常使用 if 、 else 等语句实现,而循环控制则通过 for 、 while 、 do-while 循环来完成。这些结构允许程序员控制程序的执行路径。
// 示例:条件判断与循环控制
int i = 0;
if (i > 0) {
// 当i大于0时执行的代码
} else {
// 当i不大于0时执行的代码
}
for (int j = 0; j < 10; j++) {
// 循环执行的代码块
}
本章接下来会深入到函数的定义、指针的使用、结构体的创建和使用等关键概念,为读者逐步建立起坚实的基础。
2. Socket编程与TCP/IP协议
2.1 Socket编程基础
2.1.1 Socket的定义与作用
Socket是计算机网络通信的基本构件,它提供了一种让应用程序可以发送和接收数据的抽象层。通过Socket,开发者可以不必关心底层网络协议的复杂细节,仅需按照Socket API规定的方式进行数据的读写操作,实现不同主机间进程的网络通信。
在操作系统中,Socket可以看作是应用程序和网络协议栈之间的一个接口,它不仅支持面向连接的TCP协议,也支持无连接的UDP协议,以及许多其他的协议。因此,Socket在实现网络应用如Web服务器、电子邮件客户端和文件传输服务中扮演了重要角色。
2.1.2 Socket编程模型
Socket编程模型遵循客户端-服务器架构。在这个模型中,服务器创建一个Socket,绑定到一个地址和端口上,然后监听该端口以等待来自客户端的连接请求。当服务器准备好接收请求时,它将进入阻塞状态,等待客户端的连接。一旦有客户端发起连接,服务器将接受连接,创建一个新的Socket用于与该客户端进行通信,并继续监听新的连接请求。
客户端在发起连接时,同样会创建一个Socket,然后通过服务器的地址和端口发起连接请求。一旦连接建立,客户端和服务器就可以通过各自的Socket读写数据,进行信息交换。
2.1.3 基本的Socket API
在UNIX或类UNIX系统中,Socket API提供了多种函数,使得开发者可以创建Socket,绑定地址和端口,发起或接受连接,以及进行数据的发送和接收。以下是一些基本的Socket API:
-
socket(): 创建一个新的Socket。 -
bind(): 将Socket绑定到指定的地址和端口上。 -
listen(): 在服务器端,让Socket处于监听状态,准备接受连接。 -
accept(): 服务器端接受来自客户端的连接请求。 -
connect(): 客户端发起连接到服务器。 -
send(),recv(): 用于发送和接收数据。 -
close(): 关闭Socket。
2.2 TCP/IP协议解析
2.2.1 TCP/IP协议族概述
TCP/IP是一组用于数据传输和通信的协议,它定义了如何在不同的计算机和网络之间交换信息。TCP/IP协议族包括了多种协议,其中最为人熟知的是传输控制协议(TCP)和互联网协议(IP),它们一起提供了可靠的、面向连接的数据传输服务。IP协议负责将数据包从源地址发送到目的地址,而TCP协议则确保数据包按顺序到达并进行重新组合。
此外,TCP/IP协议族还包括了其他协议,如用户数据报协议(UDP)用于无连接的数据传输,互联网控制消息协议(ICMP)用于网络设备间的控制消息传递,以及地址解析协议(ARP)用于将网络层的IP地址转换为链路层的物理地址。
2.2.2 IP协议与数据包传输
IP协议是整个TCP/IP协议族的核心,它定义了数据包在网络中的寻址和路由。每个数据包包含源地址和目的地址,这些地址是在网络中定位设备的关键。IP协议工作在网络层,负责将数据包从一个主机传输到另一个主机,不关心数据的完整性和顺序。
IP地址通常由32位(IPv4)或128位(IPv6)组成,被分为网络部分和主机部分。路由器根据数据包的目的IP地址进行路由决策,以确保数据包能够高效地到达目的地。
2.2.3 TCP协议与连接建立
TCP协议是一种面向连接的协议,它提供了一种可靠的字节流服务,保证了数据的完整性和顺序。为了实现这一点,TCP使用了称为“三次握手”的过程来建立连接,确保通信双方都准备好接收和发送数据。
TCP连接建立过程如下:
- 客户端发送一个带有SYN标志位的TCP段,初始化序列号,并等待服务器确认。
- 服务器收到这个TCP段后,回送一个带有SYN和ACK标志位的TCP段,确认客户端的序列号,并提供自己的序列号。
- 客户端收到服务器的确认后,回送一个带有ACK标志位的TCP段,确认服务器的序列号。
一旦连接建立,数据就可以在这两个端点之间双向流动。传输结束后,TCP使用四次挥手过程来关闭连接,确保所有数据都被传输且释放网络资源。
sequenceDiagram
participant C as 客户端
participant S as 服务器
C ->> S: SYN, seq=x
S -->> C: SYN, ACK, seq=y, ack=x+1
C ->> S: ACK, ack=y+1
在实际应用中,TCP协议的这些细节对于理解网络通信的可靠性和性能至关重要。通过深入分析这些协议的工作原理,开发者可以设计出高效且健壮的网络应用程序。
2.2.4 编写基本的TCP客户端和服务器代码
TCP服务器代码示例
以下是一个简单的TCP服务器端的示例代码,使用C语言编写:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
char *hello = "Hello from server";
// 创建socket文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 绑定socket到端口8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听socket
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 接受客户端连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 读取客户端发送的数据
read(new_socket, buffer, 1024);
printf("%s\n", buffer);
// 向客户端发送数据
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n");
// 关闭socket
close(server_fd);
return 0;
}
TCP客户端代码示例
紧接着是TCP客户端的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[1024] = {0};
// 创建socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 将IPv4地址从文本转换为二进制形式
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
printf("\nInvalid address/ Address not supported \n");
return -1;
}
// 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
}
// 发送数据
send(sock, hello, strlen(hello), 0);
printf("Hello message sent\n");
// 接收服务器响应的数据
read(sock, buffer, 1024);
printf("%s\n", buffer);
// 关闭socket
close(sock);
return 0;
}
在上述的TCP服务器和客户端代码中,服务器端创建了一个监听socket,并在本地的8080端口等待连接。一旦有客户端连接,服务器就会读取客户端发送的数据,然后回复一个响应,并关闭连接。客户端代码中,客户端创建了一个socket并连接到服务器的IP地址和端口上,发送一个消息然后等待服务器的响应。
这些示例代码为初学者展示了一个完整的TCP通信过程,通过实际的代码操作可以加深对Socket编程和TCP/IP协议的理解。在生产环境中,这些基础代码还需要考虑异常处理、多线程或多进程支持、安全性等因素。
3. 进程间通信(IPC)
3.1 进程间通信机制
进程间通信(IPC, Inter-Process Communication)是操作系统中不同进程之间传递消息或数据的机制。多个进程可以共享数据或协同完成工作。进程间通信机制确保在不共享内存的情况下实现通信,增加了程序的模块化和独立性。
3.1.1 管道(Pipe)
管道是最基本的IPC机制之一,它允许一个进程和另一个进程之间的单向数据流。传统的管道是半双工的,意味着数据只能单向流动。管道在Unix和类Unix系统中被广泛使用。
在Unix系统中,管道通过创建一个特殊的文件来实现,这种文件叫做管道文件,也可以在程序中用 pipe() 函数创建。 pipe() 函数的定义如下:
#include <unistd.h>
int pipe(int pipefd[2]);
该函数创建一个管道,并通过 pipefd 数组返回两个文件描述符: pipefd[0] 是读端, pipefd[1] 是写端。
管道的典型使用场景是一个进程读取另一个进程的输出:
#include <unistd.h>
#include <stdio.h>
int main() {
int pipefd[2];
pid_t cpid;
char buf;
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
在这个例子中,父进程写入管道,而子进程从管道中读取数据。这种方式是进程间通信的基础。
3.1.2 信号(Signal)
信号是系统消息,用于通知进程发生了某个事件。每个信号都有一个整数标识符,比如 SIGINT (中断信号), SIGTERM (终止信号)等。在C语言中,可以使用 signal() 函数来处理信号:
#include <signal.h>
#include <unistd.h>
void handle_signal(int sig) {
// 处理信号的逻辑
}
int main() {
// 注册SIGINT信号的处理函数
if (signal(SIGINT, handle_signal) == SIG_ERR) {
perror("signal");
exit(EXIT_FAILURE);
}
while(1) {
pause(); // 等待信号
}
}
3.1.3 消息队列(Message Queue)
消息队列是消息的链接表,存储在内核中,并由消息队列标识符标识。消息队列允许一个或多个进程向它写入消息,一个或多个进程读取队列中的消息。
消息队列的创建和操作通常使用 msgget() 、 msgsnd() 、 msgrcv() 和 msgctl() 函数:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
// 创建或打开消息队列
int msg_id = msgget(key, msgflg);
// 发送消息
struct my_msgbuf {
long mtype;
char mtext[80];
} message;
message.mtype = 1; // 消息类型
strcpy(message.mtext, "消息内容");
msgsnd(msg_id, &message, sizeof(message.mtext), 0);
// 接收消息
msgrcv(msg_id, &message, sizeof(message.mtext), 1, 0);
// 删除消息队列
msgctl(msg_id, IPC_RMID, NULL);
消息队列提供了一种灵活的方式来实现进程间通信,特别是在进程不需要实时通信的场景。
3.2 共享内存与信号量
3.2.1 共享内存的使用和管理
共享内存是最快的一种IPC机制,因为进程是直接对内存进行存取。多个进程可以挂在同一个共享内存上,进行数据交换。共享内存区独立于每个进程的地址空间,需要使用 shmget() 、 shmat() 、 shmdt() 和 shmctl() 函数来操作:
#include <sys/ipc.h>
#include <sys/shm.h>
// 创建或打开一个共享内存
int shmid = shmget(key, size, flag);
// 将共享内存附加到进程的地址空间
char *str;
str = (char*)shmat(shmid, (void*)0, 0);
// 分离共享内存
shmdt(str);
// 删除共享内存
shmctl(shmid, IPC_RMID, NULL);
3.2.2 信号量的基本概念与应用
信号量是一个计数器,用于实现进程间的同步和互斥。它和共享内存常常配合使用来控制对共享资源的访问。信号量使用 semget() , semop() , 和 semctl() 函数进行操作:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
// 创建或获取一个信号量集
int sem_id = semget(key, num_sems, flag);
// 对信号量执行P(等待)或V(信号)操作
struct sembuf sem_op;
sem_op.sem_num = 0; // 信号量集中的序号
sem_op.sem_op = -1; // P操作:wait
sem_op.sem_flg = SEM_UNDO;
semop(sem_id, &sem_op, 1);
// 删除信号量集
semctl(sem_id, 0, IPC_RMID);
3.2.3 信号量与共享内存的结合使用
共享内存允许进程共享数据,但没有同步机制来防止竞态条件。结合使用信号量,可以实现对共享内存的互斥访问。
// P操作,等待资源空闲
sem_op.sem_op = -1;
semop(sem_id, &sem_op, 1);
// 访问共享内存
// ...
// V操作,资源变空闲
sem_op.sem_op = 1;
semop(sem_id, &sem_op, 1);
信号量保证了数据的一致性,在访问共享内存前使用P操作,访问结束后使用V操作。
下一章将继续深入讨论系统调用与进程控制,包括进程的创建、管理和控制,以及进程间同步与控制。
4. 系统调用与进程控制
4.1 系统调用概述
系统调用是操作系统提供给用户程序的一组接口,它允许用户程序请求内核提供服务,如文件操作、进程创建和管理等。系统调用是用户空间和内核空间交互的基本方式之一,是操作系统安全性和稳定性的关键。
4.1.1 系统调用的作用与分类
系统调用的作用主要包括资源管理、进程控制、文件操作、通信等。分类可以按照功能进行,例如:
- 文件操作类:如
open,read,write,close等 - 进程控制类:如
fork,exec,wait,exit等 - 进程通信类:如
pipe,socket,msgget等
4.1.2 系统调用的过程与实现
系统调用的实现通常涉及用户程序发出系统调用请求、内核处理请求并返回结果的过程。具体步骤包括:
- 用户程序通过汇编语言中的中断指令
int触发系统调用。 - 控制权传递给内核中的中断处理程序。
- 内核根据系统调用号找到对应的系统调用函数。
- 执行相应的系统调用服务例程。
- 执行完成后,控制权和结果返回给用户程序。
4.2 进程的创建与管理
进程是系统进行资源分配和调度的基本单位。进程的创建、管理涉及到进程的生命周期控制。
4.2.1 fork()创建子进程
fork() 系统调用用于创建一个新的子进程,它是复制当前进程(父进程)的一个副本。具体行为如下:
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == -1) {
// fork失败
perror("fork failed");
return 1;
} else if (pid == 0) {
// 子进程
printf("Child process created with PID %d\n", getpid());
} else {
// 父进程
printf("Parent process created new child with PID %d\n", pid);
}
return 0;
}
4.2.2 exec()族函数执行新程序
exec() 族函数用于在当前进程的上下文中执行一个新的程序。它将新程序加载到内存中并覆盖当前进程的地址空间,从而实现运行新程序的目的。
4.2.3 wait()与waitpid()进程回收
当子进程结束时,父进程需要负责回收子进程的资源,这就是所谓的进程回收。 wait() 和 waitpid() 系统调用可以实现这一功能,它们会阻塞父进程直到子进程结束,并获取子进程的退出状态。
4.3 进程间同步与控制
进程间同步是为了解决多个进程在并发执行时可能发生的竞态条件问题,保证数据的一致性和系统的稳定性。
4.3.1 进程间同步机制
为了实现进程间同步,有多种机制可以使用,如互斥锁(mutexes)、信号量(semaphores)、条件变量(condition variables)等。
4.3.2 进程控制相关系统调用
进程控制相关系统调用如 nice() , setpriority() , getpriority() 等,允许进程设置自己的优先级,以及获取和修改其他进程的优先级。
4.3.3 进程组与会话控制
进程组和会话是组织进程的一种方式,用于协同工作或管理。进程组是由多个进程组成的集合,而会话可以包含多个进程组。系统调用如 setpgid() 和 setsid() 用于进程组和会话的创建和管理。
// 示例:创建一个新的会话和进程组
pid_t pid = fork();
if (pid == 0) {
// 子进程中
setsid(); // 创建一个新的会话
} else if (pid > 0) {
// 父进程中
printf("New session created with process group ID %d\n", pid);
}
通过本章节的介绍,读者应该对系统调用和进程控制有了更深入的理解,这些知识点是进行操作系统级别的编程和优化的基础。下一章节将介绍文件I/O操作,这是操作系统与外部设备交互的一个重要方面。
5. 文件I/O操作
5.1 文件描述符与打开模式
5.1.1 文件描述符的概念
文件描述符是UNIX系统中一个用于表示打开文件的抽象概念。它是一个非负整数,当进程打开一个文件或创建一个新文件时,操作系统返回一个文件描述符给进程,用于后续对文件的操作。文件描述符是一个全局的文件句柄,即使多个进程打开同一个文件,也会得到不同的文件描述符。
5.1.2 文件打开与关闭
在C语言中,打开文件的基本函数是 fopen ,其原型为:
FILE *fopen(const char *filename, const char *mode);
-
filename:文件名指针。 -
mode:打开文件的模式,如只读(“r”)、读写(“r+”)、追加写(“a”)等。
关闭文件的函数是 fclose :
int fclose(FILE *stream);
-
stream:需要关闭的文件指针。
关闭文件后,该文件描述符可以被系统回收,用于其他文件的打开。
5.1.3 文件读写操作
文件的读写通常通过 fread 、 fwrite 、 fscanf 、 fprintf 等函数进行。以 fread 为例,其原型为:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
-
ptr:指向存储数据的缓冲区。 -
size:单个数据项大小。 -
nmemb:要读取的数据项数量。 -
stream:文件流。
fwrite 与 fread 类似,但用于写入数据。
5.2 高级文件I/O技术
5.2.1 非阻塞I/O与同步
非阻塞I/O允许程序在数据不可读或不可写时立即返回,而不是阻塞等待。在C语言中,可以使用 fcntl 函数设置文件描述符为非阻塞模式。
5.2.2 I/O多路复用
I/O多路复用允许多个文件描述符的I/O操作被同时处理,提高了程序对多个文件的处理效率。常见的I/O多路复用API有 select 、 poll 和 epoll 。
-
select:支持I/O多路复用,但每次调用都需要传递所有文件描述符集合,随着文件描述符数量增多,性能下降。 -
poll:与select类似,但是poll使用链表来传递文件描述符,没有最大连接数的限制。 -
epoll:在Linux环境下,epoll相比select和poll,可以处理大量文件描述符且效率更高。
5.2.3 文件锁机制
文件锁机制用于多进程或多线程环境中的文件同步访问,避免数据损坏。在UNIX系统中,可以使用 fcntl 函数设置文件锁。
int flock(int fd, int operation);
-
fd:文件描述符。 -
operation:指定锁的行为,如LOCK_SH(共享锁)、LOCK_EX(独占锁)、LOCK_UN(解锁)等。
文件锁操作需要谨慎,因为错误的使用会导致死锁等问题。
6. 安全编程实践
编写安全的C代码是每个开发者都应当具备的能力,尤其是在处理网络服务和系统级软件时。这一章节,我们将深入探讨C语言中常见的安全漏洞,安全编码的最佳实践,以及如何利用静态和动态分析工具来提高代码质量。
6.1 编写安全的C代码
在C语言编程中,安全漏洞往往是由于内存管理不当、边界检查不严等因素引起的。本节将集中讨论这些漏洞,并提供相应的防御措施。
6.1.1 常见的C语言安全漏洞
C语言的灵活性和高效性,使得它在系统编程中占有重要地位。然而,这种自由度也带来了诸多安全隐患,比如:
- 缓冲区溢出:由于数组边界检查不严,导致数据写入超出数组分配的内存范围。
- 格式化字符串漏洞:在使用
printf等格式化函数时,输入的格式化字符串被恶意修改。 - 整数溢出:两个整数运算后超出了整数类型能够表示的范围。
- 使用不安全的函数:如
strcpy、sprintf等不安全函数,没有提供大小限制参数。
6.1.2 安全编码标准与实践
为了减少安全漏洞的发生,开发者需要遵守一些基本的安全编码标准和实践,包括但不限于:
- 使用
strncpy代替strcpy,在复制字符串时指定最大长度。 - 使用
snprintf代替sprintf,限制输出字符串的长度以避免缓冲区溢出。 - 对所有外部输入进行严格的验证和过滤,防止注入攻击。
- 使用边界检查的库函数,如
strlcpy、strlcat等。
6.1.3 静态与动态代码分析工具
开发者可以使用静态和动态代码分析工具来自动化地检测代码中的潜在安全问题。这里列举一些流行的工具:
- 静态分析工具:
- Fortify : 自动化工具,可以检测代码中的多种安全问题。
- Coverity : 一个广泛使用的静态代码分析工具,能够检测C/C++等语言中的安全漏洞。
- 动态分析工具:
- Valgrind : 用于内存泄漏检测、性能分析等。
- American fuzzy lop (AFL) : 一种模糊测试工具,它可以自动发现程序的漏洞。
6.2 防御措施与攻击模拟
防御是安全编程中的重要组成部分。了解攻击手段和进行模拟,可以帮助开发者更好地理解安全漏洞的危害,并采取相应的防御措施。
6.2.1 缓冲区溢出防御技术
为防止缓冲区溢出攻击,可以采用以下技术:
- 栈保护(StackGuard) : 在栈中加入一个“金丝雀”值,并在函数返回前检查这个值是否被改变。
- 地址空间布局随机化(ASLR) : 在程序执行时随机化内存地址,增加攻击的难度。
- 编译器安全选项 : 使用如
-D_FORTIFY_SOURCE编译器选项增强安全检查。
6.2.2 恶意软件行为分析
分析恶意软件的行为可以帮助我们了解攻击者可能采取的攻击手段。这通常涉及到:
- 行为监控 : 运行可疑软件并记录其对系统的操作。
- 网络流量分析 : 检查软件与外部通信的内容和方式。
6.2.3 攻击模拟与逆向工程入门
攻击模拟是安全测试的一个重要环节,通过模拟攻击来检验软件的安全性。逆向工程则是分析软件以发现其工作原理和潜在的安全漏洞。这些技能需要专业的知识,通常涉及的工具有:
- Metasploit : 一个安全漏洞渗透测试工具。
- Wireshark : 用于捕获和分析网络流量。
- GDB : GNU调试器,用于代码的调试和逆向工程。
通过这些工具和方法,开发者和安全研究人员可以更好地理解安全漏洞,并且在实际编码过程中采取适当的预防措施。在下一章,我们将深入探讨木马恶意软件的构造和防御策略。
简介:木马是一种恶意软件,通过伪装成合法程序来欺骗用户安装。本课程专注于使用C语言进行socket编程,构建基础但实用的木马程序。我们将涵盖C语言基础、socket通信、系统调用、文件I/O操作及安全编程等关键技术点,以帮助学生深入理解木马机制及其防御策略。
5030

被折叠的 条评论
为什么被折叠?



