Windows bezero-zerbitzariaren utilitateen funtzionaltasunarekin softwarea idaztea, 01. zatia

Agurra.

Gaur Windows-eko utilitate estandarren funtzioak betetzen dituzten bezero-zerbitzariko aplikazioak idazteko prozesua aztertu nahiko nuke, hala nola, Telnet, TFTP, etab. Java hutsean. Argi dago ez dudala ezer berririk ekarriko - utilitate horiek guztiak urte bat baino gehiago daramate arrakastaz funtzionatzen, baina uste dut denek ez dakiela zer gertatzen den kanpaiaren azpian.

Hau da, hain zuzen, ebakiaren azpian eztabaidatuko dena.

Artikulu honetan, ez arrastatu, informazio orokorraz gain, Telnet zerbitzariari buruz bakarrik idatziko dut, baina momentuz beste utilitate batzuei buruzko materiala ere badago - seriearen zati gehiagotan egongo da.

Lehenik eta behin, jakin behar duzu zer den Telnet, zertarako behar den eta zertarako erabiltzen den. Ez ditut iturriak hitzez hitz aipatuko (beharrezkoa bada, artikuluaren amaieran gaiari buruzko materialetarako esteka erantsiko dut), Telnet-ek gailuaren komando-lerrorako urruneko sarbidea ematen duela soilik esango dut. Orokorrean, hor amaitzen da bere funtzionaltasuna (nahita isilik egon nintzen zerbitzariaren atakan sartzeko; gero gehiago). Horrek esan nahi du ezartzeko, bezeroaren lerro bat onartu behar dugula, zerbitzariari pasa, komando-lerrora pasatzen saiatu, komando-lerroko erantzuna irakurri, halakorik badago, bezeroari itzuli eta bistaratu pantailan, edo, akatsak izanez gero, jakinarazi erabiltzaileari zerbait gaizki dagoela.

Aurrekoa ezartzeko, horren arabera, 2 lan-klase eta proba-klase batzuk behar ditugu, zeinetatik zerbitzaria abiaraziko dugun eta bezeroak lan egingo duen.
Horren arabera, une honetan aplikazioaren egiturak honako hauek ditu:

  • TelnetClient
  • TelnetClientTester
  • TelnetServer
  • TelnetServerTester

Ikus ditzagun horietako bakoitza:

TelnetClient

Klase honek egin ahal izan beharko luke jasotako komandoak bidaltzea eta jasotako erantzunak erakustea. Horrez gain, urruneko gailu baten ataka arbitrario batera (goian aipatu bezala) konektatu eta bertatik deskonektatu behar duzu.

Hori lortzeko, funtzio hauek ezarri ziren:

Socket helbide bat argumentu gisa hartzen duen funtzioa, konexio bat ireki eta sarrera eta irteera korronteak abiarazten dituena (korrontearen aldagaiak goian deklaratzen dira, iturri osoak artikuluaren amaieran daude).

 public void run(String ip, int port)
    {
        try {
            Socket socket = new Socket(ip, port);
            InputStream sin = socket.getInputStream();
            OutputStream sout = socket.getOutputStream();
            Scanner keyboard = new Scanner(System.in);
            reader = new Thread(()->read(keyboard, sout));
            writer = new Thread(()->write(sin));
            reader.start();
            writer.start();
        }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

Funtzio bera gainkargatuz, ataka lehenetsira konektatuz - telnet-erako hau 23 da


    public void run(String ip)
    {
        run(ip, 23);
    }

Funtzioak teklatuko karaktereak irakurtzen ditu eta irteerako socketera bidaltzen ditu - hori ohikoa da, lerro moduan, ez karaktere moduan:


    private void read(Scanner keyboard, OutputStream sout)
    {
        try {
            String input = new String();
            while (true) {
                input = keyboard.nextLine();
                for (char i : (input + " n").toCharArray())
                    sout.write(i);
            }
        }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

Funtzioak socketetik datuak jasotzen ditu eta pantailan bistaratzen ditu


    private void write(InputStream sin)
    {
        try {
            int tmp;
            while (true){
                tmp = sin.read();
                System.out.print((char)tmp);
            }
        }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

Funtzioak datuen harrera eta transmisioa geldiarazten du


    public void stop()
    {
        reader.stop();
        writer.stop();
    }
}

TelnetServer

Klase honek socket batetik komando bat jasotzeko, exekutatzeko bidaltzeko eta komandotik erantzuna socketera itzultzeko funtzionaltasuna izan behar du. Programak nahita ez ditu sarrerako datuak egiaztatzen, lehenik eta behin, "boxed telnet"-en ere zerbitzariaren diskoa formateatu daitekeelako, eta, bigarrenik, artikulu honetan segurtasun-arazoa baztertzen da printzipioz, eta horregatik ez dago. enkriptatzeari edo SSLari buruzko hitz bat.

2 funtzio baino ez daude (horietako bat gainkargatuta dago), eta, oro har, ez da oso praktika ona, baina zeregin honen ondorioetarako, dena dagoen bezala uztea egokia iruditu zait.

 boolean isRunning = true;
    public void run(int port)    {

        (new Thread(()->{ try {
            ServerSocket ss = new ServerSocket(port); // создаСм сокСт сСрвСра ΠΈ привязываСм Π΅Π³ΠΎ ΠΊ Π²Ρ‹ΡˆΠ΅ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠΌΡƒ ΠΏΠΎΡ€Ρ‚Ρƒ
            System.out.println("Port "+port+" is waiting for connections");

            Socket socket = ss.accept();
            System.out.println("Connected");
            System.out.println();

            // Π‘Π΅Ρ€Π΅ΠΌ Π²Ρ…ΠΎΠ΄Π½ΠΎΠΉ ΠΈ Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ ΠΏΠΎΡ‚ΠΎΠΊΠΈ сокСта, Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ ΠΈ ΠΎΡ‚ΡΡ‹Π»Π°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Ρƒ.
            InputStream sin = socket.getInputStream();
            OutputStream sout = socket.getOutputStream();

            Map<String, String> env = System.getenv();
            String wayToTemp = env.get("TEMP") + "tmp.txt";
            for (int i :("Connectednnr".toCharArray()))
                sout.write(i);
            sout.flush();

            String buffer = new String();
            while (isRunning) {

                int intReader = 0;
                while ((char) intReader != 'n') {
                    intReader = sin.read();
                    buffer += (char) intReader;
                }


                final String inputToSubThread = "cmd /c " + buffer.substring(0, buffer.length()-2) + " 2>&1";


                new Thread(()-> {
                    try {

                        Process p = Runtime.getRuntime().exec(inputToSubThread);
                        InputStream out = p.getInputStream();
                        Scanner fromProcess = new Scanner(out);
                        try {

                            while (fromProcess.hasNextLine()) {
                                String temp = fromProcess.nextLine();
                                System.out.println(temp);
                                for (char i : temp.toCharArray())
                                    sout.write(i);
                                sout.write('n');
                                sout.write('r');
                            }
                        }
                        catch (Exception e) {
                            String output = "Something gets wrong... Err code: "+ e.getStackTrace();
                            System.out.println(output);
                            for (char i : output.toCharArray())
                                sout.write(i);
                            sout.write('n');
                            sout.write('r');
                        }

                        p.getErrorStream().close();
                        p.getOutputStream().close();
                        p.getInputStream().close();
                        sout.flush();

                    }
                    catch (Exception e) {
                        System.out.println("Error: " + e.getMessage());
                    }
                }).start();
                System.out.println(buffer);
                buffer = "";

            }
        }
        catch(Exception x) {
            System.out.println(x.getMessage());
        }})).start();

    }

Programak zerbitzariaren ataka irekitzen du, bertako datuak irakurtzen ditu komandoaren amaierako karaktere batekin topo egin arte, komandoa prozesu berri batera pasatzen du eta irteera prozesutik socketera birbideratzen du. Dena Kalashnikov eraso-fusil bat bezain sinplea da.

Horren arabera, funtzio honen gainkarga dago ataka lehenetsi batekin:

 public void run()
    {
        run(23);
    }

Tira, horren arabera, zerbitzaria geldiarazten duen funtzioa ere hutsala da, betiko begizta eteten du, bere egoera urratuz.

    public void stop()
    {
        System.out.println("Server was stopped");
        this.isRunning = false;
    }

Ez ditut hemen proba klaseak emango, behean daude; egiten duten guztia metodo publikoen funtzionaltasuna egiaztatzea da. Dena git gainean dago.

Laburbilduz, arratsalde pare batean kontsola nagusien utilitateen funtzionamendu-printzipioak uler ditzakezu. Orain, urruneko ordenagailu batera telenetatzen dugunean, zer gertatzen ari den ulertzen dugu - magia desagertu da)

Beraz, estekak:
Iturri guztiak hemen zeuden, dira eta egongo dira
Telnet-i buruz
Telnet-i buruz gehiago

Iturria: www.habr.com

Gehitu iruzkin berria