邪恶八进制信息安全团队技术讨论组's Archiver

asm 2007-3-21 04:47

[讨论]FTP协议编写上传程序

议题作者:asm
信息来源:邪恶八进制信息安全团队

     用FTP协议中的APPE用于传输文件,在执行这个命令后,必须给传输的文件调用GetProcessHeap分配一个堆才能把文件上传上去.起初我直接发送 "APPE 1.txt",这样只是在服务器上建立一个1.txt文件.谁有FTP协议上传单个文件的代码?我到网上搜索到的都是利用wininet.lib库里的FTP网络函数,而不是使用协议,这样感觉好像知其然不知其所以然,感觉不爽.我也想到过逆向FLASHFXP等软件查看,但反汇编后的代码多达几MB,看着头晕,所以上来问问.
  还好,自己争了点气,找个简单的命令执行了一下,如图:
[attach]5597[/attach]

C代码:

/*
  FTP Agreement For Test By Asm
  2007-3-21
*/
#include <winsock.h>
#include <windows.h>
#include <stdio.h>
#pragma comment (lib,"Ws2_32")

void main(int argc, char *argv[])
{
char user[] = "USER asm\r\n"; //可以把asm换成其他的用户
char pass[] = "PASS asm\r\n";
char cmdline[] = "HELP\r\n"; //给服务器发送help命令获得帮助
char buff[1024];
int sock;
struct hostent *host; //定义hostent结构
struct sockaddr_in sin;
WSADATA WSAData;
  
if(argc != 3) //如果参数错误,则显示参数使用说明
{
  printf("Usage: %s host port\r\n",argv[0]);
  printf("Example: %s www.***.com 21\n",argv[0]);
  exit(0);
}
if(WSAStartup (MAKEWORD(2,2), &WSAData) != 0) //初始化WSAStartup
{
  printf("WSAStartup failed. %d \n",GetLastError());
  WSACleanup();
  exit(1);
}
if((host = gethostbyname(argv[1])) == 0) //从参数argv[1]处理玉米
{
  printf("cannot resolve %s\n",argv[1]);
  exit(1);
}
sin.sin_port = htons(atoi(argv[2])); //对char型的变量处理成int型,然后转换端口
sin.sin_family = AF_INET;
sin.sin_addr = *((struct in_addr *)host->h_addr_list[0]);//取出hostent结构里的IP地址
if((sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) //创建一个套接字
{
  printf("cannot setup socket %d \r\n",GetLastError());
  exit(1);
}     
if((connect(sock, (struct sockaddr *) &sin, sizeof(sin))) == SOCKET_ERROR) //开始连接
{
  printf("connect error %d \r\n",GetLastError());
  exit(1);
}
memset(buff,0,sizeof(buff)); //缓冲区清零
if(recv(sock, buff, 200, 0) == SOCKET_ERROR) //接收连接信息
{
  printf("recv nothing %d \r\n",GetLastError());
  exit(1);
}
printf("%s\r\n",buff); //打印连接到的服务器时的信息

memset(buff,0,sizeof(buff));
if(send(sock, user, strlen(user), 0) == SOCKET_ERROR) //发送用户名
{
  printf("send error %d \r\n",GetLastError());
  exit(1);
}
memset(buff,0,sizeof(buff)); //清零
if(recv(sock, buff, 200, 0) == SOCKET_ERROR) //接收服务器返回信息
{
  printf("recv nothing %d \r\n",GetLastError());
  exit(1);
}
printf("%s\r\n",buff); //打印服务器返回信息

memset(buff,0,sizeof(buff));
if(send(sock, pass, strlen(pass), 0) == SOCKET_ERROR) //发送密码
{
  printf("send error %d \r\n",GetLastError());
  exit(1);
}
memset(buff,0,sizeof(buff)); //清零
if(recv(sock, buff, 200, 0) == SOCKET_ERROR) //接收服务器返回信息
{
  printf("recv nothing %d \r\n",GetLastError());
  exit(1);
}
printf("%s\r\n",buff); //打印
  

  if(send(sock,cmdline, strlen(cmdline), 0) == SOCKET_ERROR) //发送命令用于执行
  {
   printf("send error %d \r\n",GetLastError());
   exit(1);
  }

  memset(buff,0,sizeof(buff));

  if(recv(sock, buff, 200, 0) == SOCKET_ERROR) //接收服务器返回信息
  {
   printf("recv nothing %d \r\n",GetLastError());
   return;
  }

printf("%s\r\n",buff);//打印本次接收的数据

  memset(buff,0,sizeof(buff));

  if(recv(sock, buff, 200, 0) == SOCKET_ERROR) //继续接收未接收完的数据
  {
   printf("recv nothing %d \r\n",GetLastError());
   return;
  }

printf("%s\r\n",buff); //打印本次接收的数据
closesocket(sock);
WSACleanup();
return;
}




masm32重组:

.386
.model  flat,stdcall
option  casemap:none
include  windows.inc
include  user32.inc
include  kernel32.inc
include  wsock32.inc
include  Ws2_32.inc
includelib  user32.lib
includelib  kernel32.lib
includelib  wsock32.lib
includelib  Ws2_32.lib

TCP_PORT equ 21 ;常量定义

.data
user db "USER asm",0
pass db "PASS asm",0
cmdline db "HELP",0
szHost db "www.asm32.cn",0
wsadata WSADATA<>
@stAddr sockaddr_in<>
.data?
buff db 1024 dup(?)
hSocket SOCKET ?
.code
start:
pushad
invoke WSAStartup,202h,addr wsadata
.if eax != NULL
jmp close
.endif
invoke gethostbyname,addr szHost
mov eax,[eax+hostent.h_list]
mov eax,[eax]
mov eax,[eax]
mov @stAddr.sin_addr,eax
mov @stAddr.sin_family,AF_INET
invoke htons,TCP_PORT
mov @stAddr.sin_port,ax
invoke socket,AF_INET, SOCK_STREAM,0
.if eax == SOCKET_ERROR
jmp close
.endif
mov hSocket,eax
invoke connect,hSocket,addr @stAddr,sizeof @stAddr
.if eax == SOCKET_ERROR
jmp close
.endif
invoke RtlZeroMemory,addr buff,sizeof buff
invoke recv,hSocket,addr buff,1023,0
invoke RtlZeroMemory,addr buff,sizeof buff
invoke lstrlen,addr user
invoke send,hSocket,addr user,eax,0
invoke RtlZeroMemory,addr buff,sizeof buff
invoke recv,hSocket,addr buff,1023,0
invoke lstrlen,addr pass
invoke send,hSocket,addr pass,eax,0
invoke RtlZeroMemory,addr buff,sizeof buff
invoke recv,hSocket,addr buff,1023,0
invoke lstrlen,cmdline
invoke send,hSocket,addr cmdline,eax,0
invoke RtlZeroMemory,addr buff,sizeof buff
invoke recv,hSocket,addr buff,1023,0
invoke RtlZeroMemory,addr buff,sizeof buff
invoke recv,hSocket,addr buff,1023,0
invoke MessageBox,NULL,addr buff,NULL,MB_OK

close:
invoke closesocket,hSocket
invoke WSACleanup
popad
invoke ExitProcess,NULL
end start

[s:224]


把代码发上来,只是说明自己对这个问题努力过,但是还木达到目的.各位大大帮个忙吧 *^_^*

stealthwalker 2007-3-21 19:13

不明白你的意思,你的这个也并非ftp协议啊,而只是用了ftp所支持的协议命令而已吧?没有细看你的代码,以上结论只是根据你的截图推断。当然我说的这个命令可不是登陆ftp后输入的诸如put和get命令。
感觉STOR这个协议命令应该就是上传文件的,建议你去RFC看看。

zhouzhen 2007-3-21 19:49

如果单纯为了学习协议是好的。 从开发的角度说用 wininet 是很简单的。

想知道别人怎么做? 用抓包器看看吧 :)

longinus 2007-3-21 19:55

自己实现协议也是很有趣的,我自己实现socks协议的时候遇到了很多自己曾经忽略的问题

看rfc还是比较标准的959

[url]http://www.ietf.org/rfc/rfc959.txt[/url]

asm 2007-3-21 20:31

[s:275] 看RFC文档,看到E文都都吐血,完蛋了。

bink 2007-3-21 20:37

[quote]引用第4楼asm于2007-03-21 20:31发表的 :
[s:275] 看RFC文档,看到E文都都吐血,完蛋了。[/quote]

两个选择.几M的代码和E文.[s:270]


我虽然不懂汇编,但是看你上面的代码,似乎是加载了几个WINDOWS的 Scok,之类的东西,然后用了这些类里面的一些相对比较底层的方法进行FTP连接.看你的意思似乎是想了解一些再底层点的东西...呵呵..

PS:汇编的代码和我现在写的代码没太大区别嘛..只是函数方法类那些不一样而已.呵呵.

asm 2007-3-21 22:16

很多语言都一样,就说PHP吧,和C差不多的。
[s:289]

stealthwalker 2007-3-22 08:39

STOR啊,真是的!

husheng34 2007-3-22 10:05

FTP 要 一般要两个连接才行的,

一个是控制连接,一个是数据连接,

我好像没看到数据连接,当然会传不上去了.

asm 2007-3-22 10:07

[quote]引用第8楼husheng34于2007-03-22 10:05发表的 :
FTP 要 一般要两个连接才行的,

一个是控制连接,一个是数据连接,

我好像没看到数据连接,当然会传不上去了.[/quote]

控制连接是打开了,但是数据连接怎么打开呢?请指教一下 

husheng34 2007-3-22 10:21

数据连接有几种打开方式,

控制命令发送 PORT 192,168,18,119,10,20 ( 传统方法,不利于穿过防火墙头)

表示,服务器来连接我吧,端口是 = 10 * 256 + 20 端口,

连上以后,发送传送命令,就可以通打开的数据端口发送数据了.


控制命令发送 PASV 192,168,18,119,10,20 ( 现在大多数使用方法,利于穿过防火墙头)

表示,服务器,你打开 端口,我要连你,

不发送命令,他会默认连接你的21端口

husheng34 2007-3-22 10:37

我有个半成品的FTP客户端,

不过使用了ACE库,和 STL库,产生代码很大,

你要需要.我可以给你写个C代码的.

asm 2007-3-22 10:49

  照你这么说,那是要创建两个套接字,一个用于打开控制连接,默认端口应该是21,而另外一个套接字则用于执行PORT或PASV之后,要转换成另外的端口10 * 256 + 20 ,在这个端口传输文件是吧?

[s:264] 有无例子代码捏?看代码比较实在直观 [s:269]

asm 2007-3-22 10:50

[quote]引用第11楼husheng34于2007-03-22 10:37发表的 :
我有个半成品的FTP客户端,

不过使用了ACE库,和 STL库,产生代码很大,

你要需要.我可以给你写个C代码的.[/quote]


C代码当然需要拉,那就麻烦你了..[s:269]

husheng34 2007-3-22 10:55

我现在有事,最迟最天上午发上来

husheng34 2007-3-22 15:01

// FTP.cpp : 定义控制台应用程序的入口点。
//





//=======================


//演示代码,没有错误处理,尽量简化了流程




//=========================
#include "stdafx.h"
#include <winsock.h>
#include <windows.h>
#include <stdio.h>
#pragma comment (lib,"Ws2_32")

//====以下是STL库
#include <string>
#include <vector>
#include <algorithm>


using namespace std;


//因为是演示代码,所以申明了全局变量,请不要效仿

  char Buff[1024];

  SOCKET listenFD=NULL;

//================================

  int recvbuff()
  {
  
  
    ZeroMemory(Buff,1024); //清0

    int ret = recv(listenFD,Buff,1024,0);

    printf(Buff);

    return 0;
  
  }


int main(int argc, char *argv[])
{

  
  



  struct sockaddr_in my;


  my.sin_family=AF_INET;

  my.sin_addr.s_addr=inet_addr("192.168.18.119");
  
  my.sin_port=htons(21);

WSADATA wsadata;
BOOL ThreadFlag=FALSE;
DWORD ThreadID=0;
int nRet=0;

nRet=WSAStartup(MAKEWORD(2,2),&wsadata);   //初始化



listenFD=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);


   while (1)
  {
    nRet=connect(listenFD,(struct sockaddr*)&my,sizeof (struct sockaddr));


    if (nRet!=SOCKET_ERROR)
    {
        
      break;
               
    }

  }

   recvbuff(); //接收服务器版本信息
   
   //==================================
  
   char user[] = "USER admin\r\n";
   char pass[] = "admin\r\n";

   send(listenFD,user,sizeof(user),0);

   recvbuff();


   //==========================================

  
   send(listenFD,pass,sizeof(pass),0);

   recvbuff();   


//=================================

   char type[] = "TYPE I\r\n";     

   send(listenFD,type,sizeof(type),0);  //更改传输类型为二进制

  recvbuff();

  //==========================================

  //更改路径命令是 CWD

  //================================
   char pasv[] = "PASV\r\n";     

   send(listenFD,pasv,sizeof(pasv),0);  //更改传输类型为二进制

  recvbuff();

  //=======================================
  


  //以下代码摘自我的FTP客户端,使用了 string 对象,

  //作用是处理服务器发回的 PASV 信息,得到把其中的IP和端口

  //发回的格式为  227 Entering Passive Mode (192,168,18,119,11,78)..
   
  // 192,168,18,119 = 192.168.18.119
   
  // 端口 = 11*256 + 78

  //========================================
   
   string ifstr = Buff; //把返回信息传给 string 对像,string 是STL中的字符串对象


    string::size_type pos = 0;           //长度,当整型吧,size_type是为了跨平台
   
    string::size_type slen = ifstr.find("(")+1;   // find 是指查找字符 "(" 的位置,返回长度

    string::size_type dlen = 0 ;
  
    int i = 0 ;

    for( i ; i < 4 ; i++)              //把IP地址中的 "," 换成 "."
    {
      dlen = ifstr.find(",",dlen);

      ifstr.replace(dlen,1,".");

      dlen++;      
    }
   

    string ip = ifstr.substr(slen,dlen-slen-1);  //把IP地址提出来

    //====================================     //把端口提出来
   
    string temport;

    u_short uport;

    slen = dlen;
   
    dlen = ifstr.find(",");

    temport = ifstr.substr( slen , dlen - slen); //slen 起始地址,第二参数表示 以第一个地址copy出的字节数

    uport = atoi( temport.c_str()) * 256 ;
    //=====================================

    slen = dlen + 1;

    dlen = ifstr.find(")",slen) ;

    temport = ifstr.substr(slen,dlen - slen);

    uport += atoi( temport.c_str());

    //====================================
   

    //分析返回的PASV信息完成,开始数据通道连接

    //===================================


    SOCKET ssock=NULL;

     ssock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

   
    struct sockaddr_in server;

   
    server.sin_family=AF_INET;

    server.sin_addr.s_addr=inet_addr(ip.c_str());
   
    server.sin_port=htons(uport);



   while (1)
  {
    nRet=connect(ssock,(struct sockaddr*)&server,sizeof (struct sockaddr));


    if (nRet!=SOCKET_ERROR)
    {
        
      break;
               
    }

  }



  printf("数据通道连接成功\n");






  //==============================================




  //==========================================


   char stor[] = "STOR 测试.rar\r\n";  //上传文件   

   send(listenFD,stor,sizeof(stor),0);  

  recvbuff();

  //=======================================

    FILE *fp = NULL;              //打开文件

    fp = fopen( "c:\\测试.rar","rb");     //只读打开二进制文件
   
   
  //============================================


   char filebuff[1024] ; //发送文件缓冲

  long ret;   //实际发送的字节数
  

  printf("开始发送文件......");

  while(  fread(filebuff,1,1024,fp) )  //接收文件只到全部读完
  {
    slen = 1024;

    while( slen )         //不停发送,只到全部发送完成
    {
      ret = send(ssock,filebuff,1024,0);

      Sleep(0);      

      slen -= ret;
    }

    ZeroMemory(filebuff,1024);
  
  
  }

   closesocket(ssock);

  
  printf("发送完成");

  return 0;


}

husheng34 2007-3-22 15:15

有个BUG,在上传文件后面,修改一下

printf("开始发送文件......");

  while(flen = fread(filebuff,1,1024,fp) )  //接收文件只到全部读完
  {
   
    while( flen )         //不停发送,只到全部发送完成
    {
      ret = send(ssock,filebuff,flen,0);

      Sleep(0);      

      flen -= ret;
    }

    ZeroMemory(filebuff,1024);
  
  
  }

   closesocket(ssock);

  
  printf("发送完成");

asm 2007-3-22 18:09

搞定,我把你的代码稍加修改,可以正常上传文件了 如图:

[attach]879[/attach]


谢谢拉  [s:269]


PS:结贴吧

页: [1]
© 1999-2008 EvilOctal Security Team