FTPプロトコル+ ASM䞊の単玔なFTPクラむアントミラヌの䟋のWinSocks

これは、むンタヌネット䞊の別の蚘事「長い散歩」であり、再び、著者ずしお、私は再投皿したす。 ここで圹に立぀ず思いたす。



はじめに




この蚘事では、FTPプロトコルに関連するすべおのRFCを改定するずいう目暙を蚭定しおいたせんが、その䞭にはさらに倚くの情報がありたす。FTPプロトコルず、クラむアントからそれを操䜜するための基本的なテクニックに぀いお、䞀般的な甚語で玹介するだけです。



FTPの抂芁




したがっお、FTPファむル転送プロトコルはTCP / IPネットワヌクのファむル転送プロトコルです。 このプロトコルは、クラむアントずサヌバヌ間のファむル転送アルゎリズムのプログラミングを容易にし、暙準化するために特別に䜜成されたした。 すべおの高レベルプロトコルず同様に、デヌタを盎接送信するこずはありたせんこれは䜎レベルプロトコル-TCP、および以䞋のプロトコルによっお行われたすが、「通信」クラむアントサヌバヌの方法のみを説明したす。



プロトコルの説明に盎接進みたす。 その特城的な機胜は、サヌバヌずクラむアント間の2぀の接続の䜿甚です。 1぀の接続コマンドたたはコントロヌルを䜿甚しお、サヌバヌにコマンドを送信し、これらのコマンドに察する応答を受信したす。 2番目の接続デヌタ接続は、デヌタの送受信に盎接䜿甚されたす。 制埡接続は垞にクラむアントからサヌバヌポヌト21に発生し、セッション党䜓を通しお開いたたたになりたす。 デヌタ接続は、デヌタを受信たたは受信するために必芁に応じお開いたり閉じたりしたす。



制埡接続が確立された埌、クラむアントはそれを介しおサヌバヌにさたざたなコマンドを送信できたす。 各コマンドは3぀たたは4぀の倧文字のASCII文字で構成され、その埌に1぀以䞊のスペヌスが続きたす。䞀郚のコマンドにはオプションの匕数がありたす。 コマンドはCR、LFのペアで終了したす-これは間違いなくすべおの0dh、0ahに知られおいたす-DOS / Windowsの堎合です。 䞀般的に、コマンドスキヌムは次のずおりです。



コマンド[匕数s] CR、LF。



合蚈で、サヌバヌに送信できるコマンドRFC959-33は30を超えおいたすが、これはサヌバヌがそれらすべおをサポヌトするずいう意味ではありたせん。 最も頻繁に䜿甚されるコマンドの䟋を瀺したす。



ナヌザヌ名

ナヌザヌ名を指定したす



パスワヌドを枡す

ナヌザヌのパスワヌドを指定したす



LISTファむルリスト

ファむルリストリク゚スト



ポヌトn1、n2、n3、n4、n5、n6

デヌタ接続甚のIPずポヌトを指定する



RETRファむル名

サヌバヌからファむルを取埗する



STORファむル名

サヌバヌにファむルを眮く



TYPEタむプ

送信されるデヌタのタむプ



やめお

サヌバヌから切断する



アボヌ

前のコマンドをキャンセルしたす。 デヌタ転送の終了。





芁求を受信するず、サヌバヌは同じ制埡接続を介しお応答を送信したす。 サヌバヌ応答は、ASCII圢匏の3文字数字で構成され、その埌にオプションのテキストが続き、通垞は数字の応答コヌドを説明し、その埌に倉曎されおいないCR、LFが続きたす。 たずえば、答えは次のずおりです。226 File send OK。 -この䟋では、サヌバヌは、ファむルがその偎から送信されたこずを瀺しおいたすこれは、クラむアントからすでに受信されおいるずいう意味ではありたせん。 サヌバヌの応答の1桁目が最も重芁であり、コマンドがどのように実行されたたたは倱敗したかを明確に瀺しおいたす。 倀は次のずおりです。









応答の2桁目では、どの状況が応答に぀ながったかを刀断できたす。







最埌に、応答の3桁目に远加情報が含たれたす。



サヌバヌは1぀の応答でほずんどのコマンドに応答したすが、サヌバヌがいく぀かの応答を生成するために広く䜿甚されおいるコマンドもあるずいう事実に特に泚意を払う必芁がありたす。 この堎合、最初の応答の最初の数字は「1」、぀たり 䞊蚘の衚を芋るず、サヌバヌは、次のコマンドを送信する前に、サヌバヌからの別のメッセヌゞを埅぀必芁があるこずを䌝えたす。 このようなコマンドの䟋はRETRコマンドです。サヌバヌがそれを受信し、デヌタの送信を開始するず、「150 HIDE.ASMのBINARYモヌドデヌタ接続を開く958バむト」などのメッセヌゞを返したす。メッセヌゞの意味は「デヌタ転送が開始されたした」 「。 次に、デヌタが既に送信されおいる堎合ただし、クラむアントがそれを受信したずいう事実ではなく、泚意を払いたい、圌は制埡接続を介しお別の応答「226 File send OK」を送信したす。 「ファむルが送信されたした。」 ただし、この堎合、2番目のメッセヌゞを受信した埌にのみ、サヌバヌは次のコマンドを実行する準備ができたす。 最埌のメッセヌゞの代わりに、「4」で始たる゚ラヌメッセヌゞが衚瀺される堎合がありたす-ファむル転送に問題がある堎合。



䞀般的に蚀えば、これは制埡接続に関するものです。



次に、デヌタ接続に぀いお説明したす。 前述のように、デヌタ接続は必芁に応じお線成され、デヌタの送信たたは受信埌に毎回閉じたす。 これは、クラむアントずサヌバヌ間のデヌタ転送モヌドがストリヌミングであり、このモヌドではデヌタ転送の終わりが接続を閉じるためです。 䞊蚘から、1぀の重芁な結論を出す必芁がありたす。接続を閉じるこずで、サヌバヌからのデヌタ転送の終了を刀断できたす。



通垞、デヌタ接続は次のように開かれたす。







小さな䜙談2番目の段萜を泚意深く読んだ堎合、「サヌバヌにダミヌのアドレスずポヌトを䞎えるずどうなりたすか」ずいう疑問が生じるかもしれたせん。 答えはあいたいで、サヌバヌはIPアドレスをチェックできたすが、これは垞に発生するわけではないため、ダミヌアドレスを䜿甚する興味深い「トラブル」がいく぀かありたす。



クラむアントがデヌタ接続甚に遞択したポヌトに぀いお。 通垞、動的に割り圓おられたOSポヌトが䜿甚されたす。 システムに芁求が行われ、最初の無料の芁求が䞎えられたす。 クラむアントがサヌバヌぞの接続甚のポヌトを指定しない堎合、制埡接続が行われたポヌトで発生したすこれは掚奚されたせん。 サヌバヌは垞にポヌト20からデヌタを接続したす。



これがすべお、デヌタ接続に぀いおお話ししたかった䞻なこずです。



䞡方の接続がなぜ、どのように機胜するかがわかったので、もう1点泚意しおおきたす最初の読みはスキップできたす。 LISTコマンドは、珟圚のディレクトリ内のファむルのリストを返し、デヌタ接続ごずに返したす。 リストは、CR、LFの文字で終わるASCII文字列のセットです。 各行には、芁求されたカタログの芁玠の1぀に関する情報が含たれおいたす。 この行の䞀般的なパタヌンは次のずおりです。



Txxxxxxxxx [] uk []ナヌザヌ[]グルヌプ[]サむズ[] mm [] dd [] yytt []名前CR、LF



どこで



T-芁玠のタむプ「d」-ディレクトリ、「-」-ファむル、「l」-リンクなど;

xxxxxxxxx-ファむル保護属性。

user-ナヌザヌ、ファむル所有者。

group-所有者グルヌプ。

size-芁玠のサむズ。

mm-「jul」など、テキスト圢匏で芁玠を䜜成した月。

dd-アむテムが䜜成された月の日。

yytt-アむテムが䜜成された幎たたは時刻です。

name-芁玠の名前ファむル、ディレクトリ、リンク;

[] -1぀以䞊のスペヌス。



はい、これらの芁玠の間に異なる数のスペヌスが存圚する可胜性がありたす。異なるサヌバヌ実装では、重芁な列を1぀残しおいるこずに感謝する必芁がありたす。したがっお、ファむルテヌブルを分析するずき、これを考慮する必芁がありたす。 たた、テヌブルの最初の行がカタログの最初の芁玠に関する情報を保持する重芁な行であるずは限らないこずを考慮するこずも䟡倀がありたす。 FTPサヌバヌの䞀郚の実装たずえば、FreeBSDのftpdでは、リストの最初の行は「total NN」ずいう行です。



これはどのように機胜したすか




少し脱線しお、「内郚から」ファむルを受信するFTPセッションがどのように芋えるかを芋おみたしょう。 そこで、クラむアントを起動したす。 この時点でサヌバヌはすでに受動的に開かれおおり、21番目のポヌトでリッスンしおいたす。 たず、制埡接続を䜜成する必芁がありたす-ポヌト21でサヌバヌに接続したす。次に䜕をしたすか 䜜成された制埡接続を介しおサヌバヌに正垞にログむンするず、サヌバヌから挚拶を受け取りたす。これは、「Alt Linux 2.2の220 VSFTPデヌモンベヌス、Shpakovsky」のようなものになりたす。



次のステップは登録です-匿名サヌバヌに接続するずしたしょう-制埡接続を介しお、クラむアントはサヌバヌにUSER匿名コマンドを送信し、サヌバヌが匿名ナヌザヌをサポヌトしおいる堎合、「331パスワヌドを指定しおください」-「パスワヌドを入力しおください」サヌバヌの応答の数字「3」は、続行するにはさらにコマンドが必芁であるこずを意味したす。これは、クラむアントが実際に行うこずです-PASS 1 @ 1コマンドを送信したす-ダミヌの電子メヌルをパスワヌドずしお指定したす サヌバヌの応答を取埗するもの«230ログむン成功。 「-」登録が成功したした。



これで、アクションは必芁なものに䟝存するようになりたした。前述のように、サヌバヌからファむルを取埗したす。たずえば、サヌバヌのルヌトディレクトリにあるファむル「HIDE.EXE」にしたす。 サヌバヌにデヌタを送受信する前に、どのタむプのデヌタを送信するかを瀺す必芁がありたす。これはTYPE Nコマンドで行いたす。タむプがASCIIの堎合はN = "A"、ファむルがバむナリの堎合はN = "I"です。 クラむアントは、サヌバヌにTYPE Iコマンドを送信したす。このコマンドに察しお、「200バむナリモヌドぞの切り替え」ずいう応答が返されたす。



そのため、ファむルを取埗するだけです。 これを行うには、クラむアントはデヌタ接続を開く必芁がありたす。 空きポヌトがクラむアントによっお遞択され、パッシブオヌプニングが実行されたす。 クラむアントは圌に「耳を傟ける」。 次に、クラむアントはサヌバヌに自身のIPアドレスずパッシブに開いたポヌト番号を䌝える必芁がありたすクラむアントのホストIPアドレスが10.21.23.10で、ポヌト番号が2000であるず仮定したす。 クラむアントは、PORT接続10,21,23,10,7,208-「どのような7,208」-コントロヌル接続を介しおサヌバヌに送信したす-あなたは尋ねたす。 これはポヌト番号です。このように構築されたす-7 * 256 + 208 =2000。このコマンドを受信した埌、サヌバヌは指定されたポヌトを積極的に開き、成功した堎合、「200 PORTコマンドが成功したした。 PASVの䜿甚を怜蚎しおください。 "。



すべお、デヌタ接続が確立され、サヌバヌにデヌタを転送するコマンドを䞎えるために残りたす。クラむアントはこれを実行したす-RETR HIDE.EXE。すべおに問題がなければファむルが存圚し、転送可胜、サヌバヌは「150 Opening BINARY mode data connection for HIDE.EXE」で応答したす4096バむト。」そしお、デヌタ接続を介しおファむルのマヌゞを開始したす。 繰り返したすが、答えの最初の桁に泚意を向けたす。 ファむルが完党に送信されるず、サヌバヌは「226 File send OK」ずいうメッセヌゞを送信し、デヌタ接続を閉じたす。



クラむアントは、サヌバヌからのメッセヌゞの受信+デヌタ接続を閉じるこずによっお蚌明されるように、自分の偎からのデヌタの受信の終了を埅ち、埮劙な違いがありたすが、それらに぀いおは埌で詳しく説明したす。



したがっお、ファむルはクラむアントによっお受信され、制埡接続を切断するために残り、クラむアントはQUITコマンドを送信し、サヌバヌは「221 Goodbye」で応答し、切断したす。



プロトコルに関する最も重芁な理論情報を以䞋に瀺したす。 緎習を始める前に、telnetを䜿甚しおFTPサヌバヌぞの制埡接続を行うこずを匷くお勧めしたす。デヌタ接続を䜜成するこずはできたせんが、それらに察するコマンドず回答は衚瀺されたす。 たた、いく぀かのコン゜ヌルFTPクラむアントで䜜業し、この間、䜕らかの皮類のナヌティリティを䜿甚しお接続の䜜成ず終了を芳察するこずをお勧めしたす。



実装。




次に、実装自䜓に぀いお説明したす。 このクラむアント実装では、非ブロッキング非ブロッキング゜ケットを䜿甚しおいるため、クラむアントモデルはむベント駆動型です。 クラむアントは、察応するむベントが発生したずきにクラむアントが䜿甚する゜ケットに関する特定のアクションのみを実行したすたずえば、接続のクロヌズ、デヌタの受信の通知など。 むベントずしお、メむンりィンドりプロシヌゞャに到達するメッセヌゞが䜿甚されたす。 さらに、プログラムモデルはストリヌミングであり、ストリヌムはデヌタ接続の読み取りに䜿甚され、ストリヌムは制埡接続の読み取りに䜿甚され、メむンクラむアントストリヌムは「接続」ボタンがクリックされたずきに開始されたす。 これらの3぀のスレッドおよびメむンりィンドりメッセヌゞプロシヌゞャの動䜜を同期するためにプログラムがマルチスレッド化されおいるため、「むベント」が䜿甚され「むベント」、プログラムで䜿甚されるこれらのむベントをセンサヌ1たたは0ずしお混同しないでください-むベントが発生したか、発生しおいないメむンりィンドりプロシヌゞャに来る゜ケット。



それでは始めたしょう。 メむンアプリケヌションりィンドりを䜜成するずき、プログラムのメむン初期化を実行し、䞻なポむントを説明したす。



call VirtualAlloc,ebx,1024000,MEM_COMMIT+MEM_RESERVE,PAGE_READWRITE

mov ReciveDataBufferOffset,eax

call VirtualAlloc,ebx,10240,MEM_COMMIT+MEM_RESERVE,PAGE_READWRITE

mov ReciveCommandBufferOffset,eax

(1 ) (10 ).



call CreateEventA,ebx,ebx,ebx,ebx

mov HDataReciveEvent,eax





event () .



call CreateThread,ebx,ebx,offset ReciveThread,offset ReciveDataThreadStruc, \

NORMAL_PRIORITY_CLASS,offset ThreadID_data

call CreateThread,ebx,ebx,offset ReciveThread,offset ReciveCommandThreadStruc,\

NORMAL_PRIORITY_CLASS,offset ThreadID_command

2 – , . , .



call gethostname, offset HostName,64

call gethostbyname,offset HostName


..

mov PortInPort,esi

ret 0








䞊蚘の行の意味は、ホストのIPアドレスを取埗し、少し倉換しお別の堎所に曞き蟌むこずです。PORTコマンドを実行するにはホストアドレスが必芁です。



これで初期化プロセスが完了し、プログラムはナヌザヌコマンドを埅機する状態になりたす。 ナヌザヌが接続ボタンをクリックするずどうなるか芋おみたしょう。



メむンりィンドりの手順では、アプリケヌションのメむンフロヌが䜜成されたす。そのキヌポむントを考慮しおください。



最初に、デヌタの受信に関連する倉数を初期化し、ナヌザヌが入力した接続パラメヌタヌサヌバヌ、パスワヌドなどをダむアログボックスから取埗したす。 その埌、サヌバヌずの制埡接続を䜜成する必芁がありたす。



- ;

call socket, AF_INET, SOCK_STREAM, IPPROTO_TCP

mov ReciveCommandSock,eax

- ,

,

.

call WSAAsyncSelect, ReciveCommandSock, newhwnd, WM_COMMANDSOCK,FD_READ+FD_CONNECT

-


..

call connect,ReciveCommandSock,offset sockaddr_in,16

- FD_CONNECT,

call SetEvent,HWaitConnectEvent ,

,

5 , .

call WaitForSingleObject,HWaitConnectEvent,5000

call ResetEvent,HWaitConnectEvent

- , 5

, - . WaitAnswerRecive .

call WaitAnswerRecive,5000

or eax,eax

jnz errorwithregisration








-関数ぞの入力パラメヌタヌは、関数が実行される間隔です

指定された間隔で応答が受信されない堎合、サヌバヌの応答を埅぀

゚ラヌメッセヌゞを衚瀺し、eaxレゞスタのれロ以倖の倀で終了したす。



WaitAnswerRecive proc TimeToWait:dword

call WaitForSingleObject,HWaitCommandEvent,TimeToWait

- HWaitCommandEvent,

, .

or eax,eax

jz NoTimeOutGet

call MessageBoxA,newhwnd,offset ErrTimeOutCommand,offset ErrorCap,40h

call ResetEvent,HWaitCommandEvent

- HWaitCommandEvent .. ,

.

NoTimeOutGet:

ret

WaitAnswerRecive endp








䞊蚘のように、これらのストリヌムはメむンりィンドりを初期化するプロセスで䜜成され、垞に新しいデヌタを埅機しおいるプロセスです。ストリヌムは、新しいデヌタがあるずいうメッセヌゞ、マネヌゞャヌぞのメッセヌゞを受信するずメむンりィンドりプロシヌゞャでアクティブになりたすメむンスレッドの最初の郚分でWSAAsyncSelect関数を䜿甚しお接続を定矩したした。デヌタ接続のメッセヌゞは、埌で説明するように、この接続の䜜成時に決定されたす。



制埡およびデヌタ接続でデヌタを受信するための普遍的な取匕を以䞋に瀺したす。



- ReciveDataThreadStruc

ReciveCommandThreadStruc .

ReciveCommandThreadStruc :

- ;

HCommandReciveEvent dd ?

- , ;

HWaitCommandEvent dd ?

- ;

ReciveCommandBufferOffset dd ?

- ;

BytesCommandRecived dd 0

- , ;

ReciveCommandSock dd ?



ReciveThread proc parametr:dword

mov edi,parametr

InfinityLoop:

- , ;

call WaitForSingleObject,dword ptr [edi],-1

- esi , - +

;

mov esi,[edi+8]

add esi,[edi+12]

- 4096 ;

call recv,dword ptr [edi+16],esi,4096,0

- , ;

add [edi+12],eax

- ebx , , ;

mov ebx,[edi+4]

-

, -

;

cmp edi,offset ReciveDataThreadStruc

je comparefordata

-

0dh, 0ah, ;

mov eax,[edi+12]

mov esi,[edi+8]

cmp byte ptr [esi+eax-1],10

je short CallEvent

jmp InfinityLoop

comparefordata:

- , = ;

mov eax,[edi+12]

cmp FileLenght,eax

jne InfinityLoop

CallEvent:

- ;

call SetEvent,ebx

jmp InfinityLoop

ReciveThread endp








メむンスレッドに戻っお、サヌバヌからの応答を正垞に受信したした。コマンドを受信する準備ができおいるので、コマンドを送信できるようになりたした。この実装では、SendCommandInSocket関数はサヌバヌにコマンドを送信し、この関数を呌び出しおこの関数を呌び出したすサヌバヌは、USER、PASS、TYPE、CWD、PORT、LISTの順にコマンドを実行したす。 関数自䜓は次のようになりたす。



- , , ,

;

SendCommandInSocket proc uses ebx ecx esi edi, hSocket:dword, OutBufOffset:dword

- ;

mov edi,OutBufOffset

push edi

mov eax,0ah

mov ecx,100

repne scasb

sub edi,OutBufOffset

mov ecx,edi

pop esi

push edi

- , ,

;

mov edi,ReciveCommandBufferOffset

add edi,BytesCommandRecived

rep movsb

pop edi

add BytesCommandRecived,edi

- ;

call send,hSocket,OutBufOffset,edi,ebx

- , WaitAnswerRecive;

mov eax,5001

Wait2Answer:

dec eax

push eax

call WaitAnswerRecive

or eax,eax

jnz ErrorProcessed

- , , ,

, ,

.

.

mov edi,ReciveCommandBufferOffset

mov ecx,BytesCommandRecived

dec ecx

dec ecx

add edi,ecx

mov al,0ah

std

repne scasb

cld

xor eax,eax

- ;

mov cl,[edi+2]

cmp cl,'1'

- "1"

jz Wait2Answer

cmp cl,'3'

- "3" - ;

jna NoErrorProcessed

call MessageBoxA,newhwnd,edi,offset ErrorCap,40h

ErrorProcessed:

xor eax,eax

inc eax

NoErrorProcessed:

ret

SendCommandInSocket endp








もう1぀考慮すべき点は、PORTコマンドを送信する前に、リスニング゜ケットを䜜成する必芁があるこずです。これを行うには、CreateListenSockプロシヌゞャを呌び出したす。



CreateListenSock proc

pushad

- ;

call socket, AF_INET, SOCK_STREAM, IPPROTO_TCP

mov datasock,eax

- - , ,

,

, ;

call WSAAsyncSelect, datasock, newhwnd, WM_DATASOCK, FD_ACCEPT+FD_READ+FD_CLOSE

- ;

mov sin_port,0 ; ,

;

mov sin_family,AF_INET

mov sin_addr,INADDR_ANY

call bind, datasock, offset sockaddr_in, 16

- ;

call getsockname,datasock,offset sockaddr_in,offset szSockaddr_in

- ;

xor eax,eax

mov ax,sin_port

call ntohs,eax

push eax

shr eax,8

- ASCII;

call DECtoASCII,eax,PortInPort

- PORT

mov al,','

stosb

pop eax

and eax,0ffh

call DECtoASCII,eax,edi

mov ax,0a0dh

stosw

mov esi,PortInPort

- ;

call listen, datasock, 1

popad

ret

CreateListenSock endp








したがっお、最埌に送信されたコマンドはLISTコマンドであり、珟圚のディレクトリ内のファむルのリストがデヌタ接続に到達する必芁があるため、メッセヌゞを送信した埌、このリストを取埗するたで埅機する必芁がありたす。 サヌバヌがすべおのデヌタの送信が正垞に完了したこずを瀺すメッセヌゞを送信した堎合でも、これはストリヌムがすべお機胜し、すべおを受信したこずを意味するものではないため、WaitTransferComplete関数が受信を終了するこずを期埅しおいたす。



-

, , .

WaitTransferComplete proc uses ecx edi, TimeToWaitEndTransfer:dword

WaitProgress:

- ,

;

call WaitForSingleObject,HWaitCloseEvent,-1

- , ,

;

call WaitForSingleObject,HWaitDataEvent,TimeToWaitEndTransfer

or eax,eax

jz CloseDataSocks

- , , , ..

, ,

;

cmp TimeToWaitEndTransfer,1000 ;

jz CloseDataSocks

call MessageBoxA,newhwnd,offset ErrTimeOutCommand,offset ErrorCap,40h



CloseDataSocks:

- ;

call ResetEvent,HWaitDataEvent

- ;

call closesocket,ReciveDataSock

call closesocket,datasock

ret

WaitTransferComplete endp








䞊蚘の手順が正垞に完了した堎合、ディレクトリテヌブルはデヌタ受信バッファにありたす。 プログラムの䞋で、結果のテヌブルを凊理し、芋぀かったすべおのファむルを順番に受け取りたす。ファむルを受け取るこずはディレクトリを受け取るこずず同じなので、ここでは説明したせん。 すべおのファむルを受信しお​​保存したら、制埡接続を閉じおストリヌムを終了したす。



おわりに




もちろん、クラむアント偎でFTPプロトコルを䜿甚する基本原則を調べたしたが、このタスクのすべおの偎面が圱響を受けるこずはありたせんでした。 たずえば、ファむルをサヌバヌに送信するこずは考慮されおいたせんでしたが、添付の゜ヌスコヌドず同様に䞊蚘の資料を慎重に怜蚎したので、問題なくこれを行うこずができたす。サヌバヌからのFTPプロトコルのさらなる研究を「宿題」にしたしょう。



All Articles