木马,你好!(一)教程简介

本教程目的不是教人如何去写木马,而是通过一些常见的木马知识来让大家熟悉一下一些 Windows API 的用法,并且通过了解木马编写的套路来开拓一下变成思路,以及如何防止这些常见的木马。

嗯,当然该教程既然名叫《木马,你好!》那么实际上内容也是 “你好” 这个等级的,大家算是走走过场,中间如有有碰到博主无意义的唠叨也都请无视。

以下是本教程学习的一些要求:

工具:visual studio 2010
需求:C/C++ 基础 + Windows API 基础(网络编程主要)
环境:Win7 / WinXP
测试:除开发机之外还有一台 XP/Win7 的计算机或虚拟机

虚拟机安装:

Win7 的话可以考虑用官方提供的 XP 模式(百度请搜 “win7 xp mode” 到微软官网搜索更佳),除此之外可以考虑使用 Vmware 安装虚拟机,当然如果不懂这些的话,直接在自己的计算机上也可以,只不过测试出来的效果就没有那么好了。

如果以上要求都符合(当然工具不一定非要 vs2010 ),那么我们就可以开始本次教程的旅途了。

木马(Trojan)这个名字来源于古希腊传说,特洛伊王子帕里斯访问希腊,诱走了王后海伦,希腊人因此远征特洛伊。围攻9年后,到第10年,希腊将领奥德修斯献了一计,就是把一批勇士埋伏在一匹巨大的木马腹内,放在城外后,佯作退兵。特洛伊人以为敌兵已退,就把木马作为战利品搬入城中。到了夜间,埋伏在木马中的勇士跳出来,打开了城门,希腊将士一拥而入攻下了城池。后来,人们在写文章时,就常用“特洛伊木马”这一典故,用来比喻在敌方营垒里埋下伏兵里应外合的活动。

而到了现在,Trojan 一词的特洛伊木马本意是 “特洛伊的” ,即代指特洛伊木马。如果你已经了解了这个故事,那么你也差不多理解了木马到底是干什么的。就如开始故事里面说的那样,计算机”木马”就是悄悄的潜伏在你的计算机中干坏事的那个木马。

目前来说,“木马” 程序是目前比较流行的病毒文件,与一般的病毒不同,它不会自我繁殖,也并不“刻意”地去感染其他文件,它通过将自身伪装吸引用户下载执行,向施种木马者提供打开被种者计算机的门户,使施种者可以任意毁坏、窃取被种者的文件,甚至远程操控被种者的计算机。

如果你已经学习了 Windows API 中的网络编程部分,并对该部分有了一定的掌握(这个是必须的,如果没有请查看博主的《Windows API 教程(九) 网络编程》),那么我们就可以简单的来聊聊木马了。

当你第一次编写出连带服务端还有客户端的 socket 通信程序之后,你有想过有通过这个技术来写些什么东西吗?事实上,不管你想的东西有多少,博主都可以明确的告诉你:这东西的用途远远要比你想象的多。

首先呢,一个很简单的应用就是用来做木马。为什么这么说?原因很简单,木马程序对 GUI (窗口编程)没有什么要求,因为一般都是写着自己用。而且木马程序的思路其实很简单,就只是服务端跟客户端的连接之后的简单应用。

印象不深的各位可以简单的浏览一下博主的代码(简单的socket通信实例),这里是一个很简单的 socket 通信的例子,中间有一个简单的交互过程,即在客户端连接上来的时候服务端发送一个字符串下来,而客户端第一时间就能收到这个服务端下发的字符串,随后返回一个字符。

那么博主这里在那个代码的基础上稍微修改一下:

server.c

#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

void main()
{
    int err; // 错误信息
    int len;

	char sendBuf[100] = {0}; // 发送至客户端的字符串
	char recvBuf[100] = {0}; // 接受客户端返回的字符串
	char *ip;

    SOCKET sockServer;     // 服务端 Socket
    SOCKADDR_IN addrServer;// 服务端地址
    SOCKET sockClient;     // 客户端 Scoket
    SOCKADDR_IN addrClient;// 客户端地址

    WSADATA wsaData;       // winsock 结构体
    WORD wVersionRequested;// winsock 的版本

    // 配置 Windows Socket版本
    wVersionRequested = MAKEWORD( 2, 2 );

    // 初始化 Windows Socket
    err = WSAStartup( wVersionRequested, &wsaData );

    if ( err != 0 )
    {
        // 启动错误,程序结束
        return;
    }

    /*
     * 确认WinSock DLL支持2.2
     * 请注意如果DLL支持的版本大于2.2至2.2
     * 它仍然会返回wVersion2.2的版本
     */

    if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )
    {
        // 启动错误,程序结束
        WSACleanup(); // 终止Winsock 2 DLL (Ws2_32.dll) 的使用
        return;
    }

    // 定义服务器端socket
    sockServer = socket(AF_INET, SOCK_STREAM, 0);

    //  设置服务端 socket
    addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 本机IP
    addrServer.sin_family = AF_INET;                   // 协议类型是INET
    addrServer.sin_port = htons(6000);                 // 绑定端口6000

    // 将服务器端socket绑定在本地端口
    bind(sockServer, (SOCKADDR *)&addrServer, sizeof(SOCKADDR));

    // Listen 监听端口
    listen(sockServer, 5); // 5 为等待连接数目

    printf("服务器已启动:n监听中...n");

    len = sizeof(SOCKADDR);

    while (1)
    {
        // accept 会阻塞进程,直到有客户端连接上来为止
        sockClient = accept(sockServer, (SOCKADDR *)&addrClient, &len);
		// 获取ip地址
		ip = inet_ntoa(addrClient.sin_addr);
		// 输出连接提示
		printf("-- IP %s 连接到服务端n", ip);
        // 当客户端连接上来时, 拼接如下字符串
        sprintf(sendBuf, "欢迎 ip: %s 的用户连接, 这里是 Lellansin 的服务器", ip);
		// 向客户端发送字符串
		send(sockClient, sendBuf, strlen(sendBuf) + 1, 0);

		while (1)
		{
			// 获取客户端返回的数据
			recv(sockClient, recvBuf, 100, 0);
			// 打印客户端返回的数据
			printf("-- %s: %sn", ip, recvBuf);
			// 输入提示符
			printf(">>");
			// 接收用户的输入
			scanf("%s",sendBuf);
			// 向客户端发送字符串
			send(sockClient, sendBuf, strlen(sendBuf) + 1, 0);
		}

        // 关闭socket
        closesocket(sockClient);
    }
}

client.c

#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

void main()
{
	int err = 0;
	char message[256] = {0};
	char recvBuf[100] = {0};

	SOCKET sockClient; // 客户端 Scoket
	SOCKADDR_IN addrServer; // 服务端地址

	WSADATA wsaData;
	WORD wVersionRequested;

	wVersionRequested = MAKEWORD( 2, 2 );

	err = WSAStartup( wVersionRequested, &wsaData );

	if ( err != 0 )
	{
		return;
	}

	if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )
	{
		// 启动错误,程序结束
		WSACleanup( );
		return;
	}

	// 新建客户端 scoket
	sockClient = socket(AF_INET, SOCK_STREAM, 0);

	// 定义要连接的服务端地址
	addrServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  // 目标IP (127.0.0.1是本机地址)
	addrServer.sin_family = AF_INET;                           // 协议类型是INET
	addrServer.sin_port = htons(6000);                         // 连接端口1234

	// 让 sockClient 连接到 服务端
	connect(sockClient, (SOCKADDR *)&addrServer, sizeof(SOCKADDR));

	while(1)
	{
		// 从服务端获取数据
		recv(sockClient, recvBuf, 100, 0);
		// 打印数据
		printf("-- Server: %sn", recvBuf);
		// 输入提示符
		printf(">>");
		// 接收用户的输入
		scanf("%s", message);
		// 发送数据到服务端
		send(sockClient, message, strlen(message) + 1, 0);
	}

	// 关闭socket
	closesocket(sockClient);
	WSACleanup();
}

测试效果图:

socket_chat

大家可以看到,这样客户端与服务器端就简单的交互了。而这个过程稍微修改一下就可以变成一个客户端与服务端交互的过程,例如让服务端发送一条命令到客户端,然后客户端执行这个命令,并且返回给服务端然后继续等待服务端的下一个命令。这个时候如果客户端程序运行在要控制的计算机上,那么就已经可以说是一个 “木马” 了。

相信看到这里,你的心中大概已经有些思路了,那么建议你停下来好好的把握住脑海中的思路自己尝试一下。

文章索引

下一讲:木马,你好!(二)最简单的木马

8 thoughts on “木马,你好!(一)教程简介

    • 这个后面的文章也有讨论过。单间点说,博主认为被控端是 server 的话如果被控端的 ip 改变就容易找不回来了,所以个人比较喜欢反过来,让被控端自己找上来。

      • 一开L君就没玩过木马,最初最初,木马确实是直连的,这都是远古黑科技,然后就有了杀毒软件开始选择性的监控外部直连端口,一咬一个准,大家觉得这样玩太弱智。就开始选择回弹木马,这样有什么好处,1,绕过杀毒软件外部直连的监控,毕竟get比较好监控,post比较难监控,再者,也不用去记肉鸡的每个ip地址了,用的就是花生壳动态域名,然后接下来就是ssdt的攻防,以上都是6年前的科技

      • 说的都没错,我确实很少玩木马,做这个教程也只是想给 API 的教程做个没窗口方向的应用。这个教程也就是 “你好” 级别的,给很多有兴趣的同学入门的。很多内容是去年写的,楼上说的这些内容,我一直觉得好像是很容易知道的东西,所以教程里面也没有专门讨论这个, 全都直接说实现然后上代码了,多谢补充。顺便,应该只不是6年前,感觉都是至少10年前的东西了。

      • 但是现在反弹式被杀的太严重,很多木马又回到了最早的直接链接+IP地址发送者的方式了

  1. 博主,我是小白,想请教一下我运行了server和client但无法输入任何内容,为什么?

留下评论