Все началось еще лет 10 назад, когда я впервые наткнулся на статью где было описано как эксперт по 3D-технологиям вместил сильно обфусцированный код рейтрейсера на C++ в размеры своей визитки.

Внимание на код - он полностью рабочий!
Внимание на код - он полностью рабочий!

Вот такой код:

 #include <stdlib.h>   // card > aek.ppm
   #include <stdio.h>
   #include <math.h>
   typedef int i;typedef float f;struct v{
   f x,y,z;v operator+(v r){return v(x+r.x
   ,y+r.y,z+r.z);}v operator*(f r){ return
   v(x*r,y*r,z*r);}f operator%(v r){return
   x*r.x+y*r.y+z*r.z;}v(){}v operator^(v r
   ){return v(y*r.z-z*r.y,z*r.x-x*r.z,x*r.
   y-y*r.x);}v(f a,f b,f c){x=a;y=b;z=c;}v
   operator!(){return*this*(1/sqrt(*this%*
   this));}};i G[]={133022, 133266,133266,
   133022, 254096, 131216, 131984, 131072,
   258048,};f R(){return(f)rand()/RAND_MAX
   ;}i T(v o,v d,f&t,v&n){t=1e9;i m=0;f p=
   -o.z/d.z; if(.01<p)t=p, n=v(0,0,1),m=1;
   for(i k=19;k--;)for(i j=9;j--;)if(G[j]&
   1<<k){v p=o+v(-k,0,-j-4);f b=p%d,c=p%p-
   1,q=b*b-c;if(q>0){f s=-b-sqrt(q);if(s<t
   &&s>.01)t=s,n=!(p+d*t),m=2;}}return m;}
   v S(v o,v d){f t;v n;i m=T(o,d,t,n);if(
   !m)return v(.7,.6,1)*pow(1-d.z,4);v h=o
   +d*t,l=!(v(9+R(),9+R(),16)+h*-1),r=d+n*
   (n%d*-2);f b=l%n;if(b<0||T(h,l,t,n))b=0
   ;f p=pow(l%r*(b>0),99);if(m&1){h=h*.2;
   return((i)(ceil(h.x)+ceil(h.y))&1?v(3,1
   ,1):v(3,3,3))*(b*.2+.1);}return v(p,p,p
   )+S(h,r)*.5;}i main(){printf("P6 512  "
   "512 255 ");v g=!v(-6,-16,0),a=!(v(0,0,
   1)^g)*.002,b=!(g^a)*.002,c=(a+b)*-256+g
   ;for(i y=512;y--;)for(i x=512;x--;){v p
   (9,9,9);for(i r=64;r--;){v t=a*(R()-.5)
   *99+b*(R()-.5)*99;p=S(v(17,16,8)+t,!(t*
   -1+(a*(R()+x)+b*(y+R())+c)*16))*3.5+p;}
   printf("%c%c%c",(i)p.x,(i)p.y,(i)p.z);}}

После компиляции:

 c++ -O3 -o card card.cpp

Генерировал у автора вот такую картинку:

Я тоже захотел себе что-то такое, но поскольку занимаюсь все же больше серверами чем 3D-графикой и Java, а не C++ — решил что будет круто уместить на стороне визитки простейший HTTP-сервер на Java.

Вместе с запуском и компиляцией.

Еще при наличии графического окружения будет запущен браузер.

Плюс немного криптографии для защиты от подделки.

Весь код уместился в 18 строк, выровненных по ширине так чтобы влезть в размеры визитки:

Вбиваете код с визитки в любимый редактор, сохраняете файл как vcard.sh и запускаете:

chmod +x ./vcard.sh
./vcard.sh

Результат:

FreeBSD 14 и Java 17
FreeBSD 14 и Java 17
Solaris (OpenNexenta) и JDK 22
Solaris (OpenNexenta) и JDK 22
Ubuntu и Java 1.8
Ubuntu и Java 1.8

Локально запустится простейший вебсервер, который отдаст текстовую страничку с текстом и контактами. При наличии GUI  — запустится еще и браузер по-умолчанию, с автоматическим открытием страницы этого сервера.

И все это в 18 строк кода.

Да, еще будет нужен любой Linux/BSD/MacOS/Solaris и любая версия JDK начиная с 1.8 на машине.

Поддержку запуска на Windows делать не стал (хотя это и возможно технически), но можно спокойно запустить в WSL .

Чтобы вы не мучились с вводом кода с картинки, вот текстовая версия:

#!/bin/sh
t=$(mktemp -d);e=$(realpath  $0);sed '1,4d' $0|sed -e 's/p /public /g ; s/i /import /g' \
 -e 's/j\./java./g ; s/E!/Exception/g ; s/U8!/UTF-8/g ; s/RE?/ResponseHeaders/g'>$t/Yo.java
cd $t;javac -XDignore.symbol.file -cp . Yo.java && java -cp . Yo $e;exit 0
i com.sun.net.httpserver.*;i j.awt.Desktop;i j.io.*;i j.util.*;i j.net.*;i j.nio.file.*;
i java.security.MessageDigest;i javax.crypto.Cipher;i javax.crypto.spec.SecretKeySpec;
p class Yo{p static void main(String[]args)throws E!{Cipher dcipher=Cipher.getInstance("AES");
dcipher.init(2,new SecretKeySpec(Arrays.copyOf(MessageDigest.getInstance("SHA-1").digest(
new String(Files.readAllBytes(Paths.get(args[0]))).replaceFirst("ED=\"([^<]*)\";","")
.trim().getBytes()),16),"AES"));String ddata=new String(dcipher.doFinal(Base64.getDecoder()
.decode(ED)),"U8!");int p=8000;String h="0x7f000001";HttpServer s=HttpServer.create()
.createContext("/",(HttpExchange t)->{String r=String.format(ddata,System.nanoTime());t.getRE?()
.set("Content-type","text/plain;charset=U8!");t.sendRE?(200,r.getBytes("U8!").length);
try(OutputStream os=t.getResponseBody()){os.write(r.getBytes("U8!"));}}).getServer();
s.bind(new InetSocketAddress(p),1);new Timer().schedule(new TimerTask(){public void run(){
if(!Desktop.isDesktopSupported()){System.out.println(ddata);return;}try{Desktop.getDesktop()
.browse(new URI("http://"+h+":"+p));}catch(E! e){throw new RuntimeE!(e);}}},2000);s.start();}
static String ED="TfPrCIlXEUInGJPpr4++hQfa2Whq4RFzdbFP5C4s/s8=";}

Ну разве не прелесть?

Как это работает

Тут используется связка из заголовочного shell-скрипта и слегка обфусцированного кода на Java. Еще я не стал кодировать весь блок на Java полностью в HEX-строку, чтобы визуально оставалось ощущение исходного кода.

Начнем с заголовочного скрипта:

#!/bin/sh
t=$(mktemp -d);e=$(realpath  $0);sed '1,4d' $0|sed -e 's/p /public /g ; s/i /import /g' \
 -e 's/j\./java./g ; s/E!/Exception/g ; s/U8!/UTF-8/g ; s/RE?/ResponseHeaders/g'>$t/Yo.java
cd $t;javac -XDignore.symbol.file -cp . Yo.java && java -cp . Yo $e;exit 0

Самая первая строчка:

#!/bin/sh

Это shebang, стандартное для Unix указание на используемый интерпретатор, про него и так все знают.

Дальше происходит создание временного каталога в /tmp и присваивание его имени переменной в скрипте:

t=$(mktemp -d);

Затем получение скриптом собственного имени с полным путем:

e=$(realpath  $0);

Чтение скриптом самого себя, с отрезанием первых 4х строк - чтобы получить блок кода на Java:

sed '1,4d' $0

Дальше начинается pipe, в котором результат предыдущей команды передается на вход следующей:

|sed -e 's/p /public /g ; s/i /import /g' \
 -e 's/j\./java./g ; s/E!/Exception/g ; s/U8!/UTF-8/g ; s/RE?/ResponseHeaders/g'>$t/Yo.java

а результат всех преобразований записывается в файл Yo.java, в том самом временном каталоге.

Малоизвестная опция -XDignore.symbol.file отключает предупреждение об использовании системных классов JDK (com.sun.net.httpserver.*) в проекте — в 1.8 версии классы встроенного в JDK HTTP-сервера еще считались системными.

Запуск с передачей полного пути оригинального скрипта для последующего его чтения из Java-кода:

java -cp . Yo $e

Сам код после деобфускации и форматирования выглядит уже вот так:

import com.sun.net.httpserver.*;
import java.awt.Desktop;
import java.io.*;
import java.util.*;
import java.net.*;
import java.nio.file.*;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Yo {
    public static void main(String[] args) throws Exception {
        Cipher dcipher = Cipher.getInstance("AES");
        dcipher.init(2, 
        new SecretKeySpec(Arrays.copyOf(
        MessageDigest.getInstance("SHA-1").digest(
                new String(Files.readAllBytes(Paths.get(args[0])))
                .replaceFirst("ED=\"([^<]*)\";", "")
                        .trim().getBytes()), 16), "AES"));
        String ddata = new String(dcipher.doFinal(Base64.getDecoder()
                .decode(ED)), "UTF-8");
        int p = 8000;
        String h = "0x7f000001";
        HttpServer s = HttpServer.create()
                .createContext("/", (HttpExchange t) -> {
                    String r = String.format(ddata, System.nanoTime());
                    t.getResponseHeaders()
                            .set("Content-type", "text/plain;charset=UTF-8");
                    t.sendResponseHeaders(200, r.getBytes("UTF-8").length);
                    try (OutputStream os = t.getResponseBody()) {
                        os.write(r.getBytes("UTF-8"));
                    }
                }).getServer();
        s.bind(new InetSocketAddress(p), 1);
        new Timer().schedule(new TimerTask() {
            public void run() {
                if (!Desktop.isDesktopSupported()) {
                    System.out.println(ddata);
                    return;
                }
                try {
                    Desktop.getDesktop()
                            .browse(new URI("http://" + h + ":" + p));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }, 2000);
        s.start();
    }
    static String ED = "1AtzGU0uq7J7DHPdjdJJ5JJDiwQi8mElIDOjuRK0DEU=";
}

Тут уже большая часть логики вполне очевидна, поэтому раскрою лишь два самых сложных фрагмента.

Криптография

Когда я только начинал думать над реализацией этой штуки, уже было ясно что нужен какой-то неочевидный контроль целостности:

исходный код очевидно будут пересылать через сообщения, в виде постов или по почте, что легко его сломает.

Поэтому хотелось хоть какую-то защиту от подделки содержимого, чтобы компьютерные дети не добавили патч Брамина в самое интересное место, а индийский паренек не подменил авторство и мои контакты на свои, ради строчки в резюме.

Задачу усложнял факт передачи открытых исходников и ограничение по размерам, но видимо получилось:

 static String ED = "1AtzGU0uq7J7DHPdjdJJ5JJDiwQi8mElIDOjuRK0DEU=";

Именно тут находится текст:

We write software. @alex0x08

На каждую попытку как-то подменить содержимое (включая заголовок) будет выдаваться вот такая ошибка:

Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
	at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:981)
	at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1062)
	at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
	at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
	at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202)
	at Yo.main(Yo.java:6)

Получается код сам себя защищает от подделки.

0x7f000001

Вторым неочевидным моментом является вот такой странный адрес хоста:

 String h = "0x7f000001";

который используется при формировании ссылки для открытия браузером:

Desktop.getDesktop().browse(new URI("http://" + h + ":" + p));

Такое применение однозначно говорит о том что адрес очень даже стандартный, поскольку проходит как стадию валидации на стороне Java при формировании объекта URI, так и валидацию на стороне запускаемого браузера.

Вообщем это просто нотация, вариант написания IP-адреса 127.0.0.1, обозначающего loopback (петлю) — внутренний интерфейс, к которому можно подключиться локально, а не из сети.

Вот тут больше примеров различных вариантов написания IP-адресов, уверен — удивит даже бывалых админов.

Более фривольный оригинал статьи находится в нашем блоге, где мы подробно рассказываем об ужасах разработки, вгоняя в краску даже опытных и бывалых.

0x08 Software

Мы небольшая команда ветеранов ИТ-индустрии, создаем и дорабатываем самое разнообразное программное обеспечение, наш софт автоматизирует бизнес-процессы на трех континентах, в самых разных отраслях и условиях.

Оживляем давно умершее, чиним никогда не работавшее и создаем невозможное - затем рассказываем об этом в своих статьях.

Комментарии (40)


  1. Kahelman
    14.06.2024 21:18
    +7

    Прикольно но не совсем web server. Так можно строить bash скрипт с установкой Apache и запуском со страницей автора. Было бы круто именно plain code получить


  1. jonic
    14.06.2024 21:18
    +34

    Сомнительно честно говоря, ожидал визитку раздающую wifi.


    1. alex0x08 Автор
      14.06.2024 21:18

      Это больше к "железячникам", полагаю для батареи все равно места не хватит ;)


      1. aamonster
        14.06.2024 21:18
        +2

        Я прикидывал, 2016 можно врезать в отверстие в плате, но визитка будет толстенькая (текстолит 1.6мм + компоненты... Хотя опять же их можно в выфрезированные отверстия заглубить).


        1. MaFrance351
          14.06.2024 21:18

          Давным-давно видел электронную карту с дисплеем и псевдосенсорной кнопкой для генерации паролей, по габаритам ничем не отличалась от обычной пластиковой кредитки. Тут, конечно, будет потолще, но всё равно габариты будут не запредельными. Так что можно и попробовать...


          1. aamonster
            14.06.2024 21:18

            Угу, но там, вероятно, какой-то редкий источник питания, а cr2016 – типовой и лежит в магазинах.


      1. jonic
        14.06.2024 21:18
        +3

        Тем и инетреснеее


      1. 15432
        14.06.2024 21:18
        +1

        Для солнечной - вполне хватит, имел визитку-калькулятор


      1. sysardex
        14.06.2024 21:18

        Можно оставить контакты для подключения внешнего питания)


  1. ivanuil
    14.06.2024 21:18
    +18

    Какой подход лучше, монолитная визитка или визитка на микросервисах?


    1. Acilez
      14.06.2024 21:18
      +1

      На микросхеме


    1. ihouser
      14.06.2024 21:18
      +3

      Для микросервисов понадобится расчлененная визитка на микровизитки. Монолитная удобнее.


      1. aamonster
        14.06.2024 21:18
        +1

        Если сделать пазлом с контактами между тайлами – будет круть неимоверная.


  1. codecity
    14.06.2024 21:18
    +4

    Генерировал у автора вот такую картинку:

    Я сначала не понял что нужно направить консольный вывод в .bmp файл, типа ./card > image.bmp. Довольно прикольно.


  1. RedWolf
    14.06.2024 21:18
    +6

    HttpServer.create(), пфффф

    Можно было все завернуть в свою либу и обойтись одной строкой.


    1. IvanG
      14.06.2024 21:18
      +20

      Дак докер ран и все дела... Тоже ожидал хардварного решения вопроса, клик бейт


  1. red_dragon
    14.06.2024 21:18

    Интересно, а в нынешнее время, все ещё в ходу бумажные визитки?


    1. RalphMirebs
      14.06.2024 21:18
      +1

      от страны зависит, где-то они даже обязательны


      1. red_dragon
        14.06.2024 21:18
        +4

        Опа. Хотелось бы услышать про страны, в которых визитки обязательны.


        1. RalphMirebs
          14.06.2024 21:18

          Япония. Понятно, в тюрьму не посадят за отсутствие, но расценить, как проявление неуважения запросто. С последующим отказом от переговоров со стороны консервативного делового партнёра.


    1. geher
      14.06.2024 21:18
      +3

      Очень даже в ходу. Раздавать бумажные визитки проще и быстрее, чем диктовать контакт или морочиться со связыванием двуух смартфонов.

      Причем с точки зрения партнеров это безопаснее, чем флэшки и даже CD.


      1. red_dragon
        14.06.2024 21:18

        Ну, про скорость, вопрос спорный. Если у вас визитки всегда под рукой, как смартфон (что скорее всего не так), то может быть быстрее. Вопрос только в том, вспомнит ли ваш "партнёр" про эту визитку позже. И при этом, у него в телефоне белый список, например. Пассаж про опасность флешек и даже CD, извините, не понял.

        До сих пор, где-то в недрах рюкзака, таскаю с десяток визиток, как рудимент прошлого. За последние лет десять, ни одной не отдал, хоть и предлагал иногда. Проще продиктовать телефон. И, главное, надёжнее.


        1. Paranoich
          14.06.2024 21:18
          +2

          Проще продиктовать телефон

          1. Не все помнят телефон секретарши или приёмной. Тем более их бывает несколько. Визитки и корпоративные ведь бывают.

          2. Сложно представить председателя совета директоров международной корпорации на деловой встрече, который лезет в карман за айфончиком, дабы записать номер собеседника — заместителя председателя правительства области.

          3. С чего бы это депутат городской думы телефон диктовать будет. А на встрече с избирателями как? В мегафон номер орать?

          4. Лично мне странно доставать телефон и записывать номер сантехника, которого может больше и не увижу никогда. Неделю назад сантехник приходил, крутил чего-то. Предложил номер записать. Зачем он мне? Взял визитку, примагнитил к холодильнику, где в случае чего любой его найдёт. Или в случае потопа созваниваться и номера диктовать родным?

          5. Насчёт того, что где-то в каких-то странах визитки обязательны — не слышал. Но знаю, что у (наших местных по крайней мере) участковых есть правило — визитку всем желающим. И пусть это всего лишь напечатанная на принтере бумажка, но это лучше, чем номер участкового в телефоне.

          И вообще свинство — впаривать предлагать свои услуги или предложения и просить при этом записать номер в телефон.


          1. aamonster
            14.06.2024 21:18

            Визитку удобнее не забирать, а фотографировать.
            Если визитка корпоративная – imho было бы неплохо на ней печатать среди прочего qr-код со ссылкой на сайт.


            1. geher
              14.06.2024 21:18
              +1

              qr-код со ссылкой на сайт.

              Или с контактными данными.

              Впрочем, "о существовании кнопки '+' семейство Кенги не догадывалось", в смысле очень редко вижу визитки с QR кодом.


        1. geher
          14.06.2024 21:18

          Если у вас визитки всегда под рукой, как смартфон (что скорее всего не так), то может быть быстрее.

          У многих визитки как раз всегда под рукой. У разного рода менеджеров в разных организациях, у сантехников, электриков и прочих приходящих специалистов разного профиля. Сразу как приходишь в кабинет, или как приходит спец что-то делать, так сразу выхватывается визитка и передается клиенту.

          Вопрос только в том, вспомнит ли ваш "партнёр" про эту визитку позже.

          Понадобится - вспомнит, в телефон забьет и сохранит (в случае приходящего спеца сохраняют обычно магнитом на холодильник, если спец хороший оказался, или в мусорное ведро, если плохой). Ну а не понадобится, так визитка не столь дорога. Тут, наверное, как с рекламой работает.

          Пассаж про опасность флешек и даже CD, извините, не понял.

          Одно время было "круто" вместо визитки давать флэшку или CD (обычно маленький, 80 мм, или даже в формате визитки, прямоугольник с закругленными краями) с данными о себе или компании. Но человек с развитой здоровой паранойей не станет совать неизвестный носитель себе в компьютер, ибо мало ли что там, вдруг вирус злой и совсем новый.

          За последние лет десять, ни одной не отдал, хоть и предлагал иногда. Проще продиктовать телефон. И, главное, надёжнее.

          Тут, наверное, все зависит от количества новых контактов. Если их много, то язык отсохнет диктовать, а пальцы - записывать. Если они единичные, то, действительно, проще и надежнее продиктовать телефон.

          Если что, у меня визиток своих нет, не раздаю, но довольно часто получаю.


  1. nikolz
    14.06.2024 21:18
    +8

    Это не сервер на визитке, а bat файл на джава для запуска сервера в OS.

    Полагаю, что сервер на визитке это код, который запустится на bare-metal.


  1. adron_s
    14.06.2024 21:18
    +7

    Для этого просто нужно использовать подходящие инструменты, например bash.

    while true; do echo V2Ugd3JpdGUgc29mdHdhcmUuIEBhbGV4MHgwOAo= | base64 -d | nc -l -p 8000 -q 1; done

    Или, если хочется загадочности, то вот так:

    echo d2hpbGUgdHJ1ZTsgZG8gZWNobyBWMlVnZDNKcGRHVWdjMjltZEhkaGNtVXVJRUJoYkdWNE1IZ3dPQW89IHwgYmFzZTY0IC1kIHwgbmMgLWwgLXAgODAwMCAtcSAxOyBkb25lCg== | base64 -d | sh

    Или даже так:


  1. Rubilnik
    14.06.2024 21:18
    +2

    Ну не знаю, окажется ещё что Ваш сервер с бэкдором))


    1. alex0x08 Автор
      14.06.2024 21:18
      +2

      Про бекдоры (реверс-шелл) у меня есть в другой публикации. Вставлять бекдор в собственную визитку - моветон.


  1. jpegqs
    14.06.2024 21:18
    +3

    У меня есть HTTP сервер на Си, и не прямоугольником, а ASCII артом.


  1. rendov
    14.06.2024 21:18
    +8

    У визитки-рендера есть смысл: она показывает скилл человека в этой области. Демонстрирует настолько хорошо он понимает всю эту тему, что смог без спец-библиотек сделать впечатляющее изображение, компилирующееся хоть на винде, хоть на линуксе, хоть на маке.

    А в чем смысл вашей визитки? Напихать побольше либ на джаве, чтоб сделать server.create(), обфускацировать код и назвать это сервером на визитке? Ну да, поиграли с криптографией, но ей богу, раз вы используете bash, то что мешало на нем и написать сервер и использовать всякие base64 и sha1sum? А если хотите скилл джавы показать, то зачем платформозависимость? В общем странно все это.


    1. alex0x08 Автор
      14.06.2024 21:18

      Эм, проблема в том что на Хабре матом ругаться не принято - тут вроде как высшее айтишное общество, фраки, шампанское и тонкий разговорный французский. Поэтому если хотите устроить срач - велкам в мой бложег, там можно не сдерживаться.


  1. grigorra
    14.06.2024 21:18
    +5

    По моему, в наше время такая визитка больше бы подошла пентестерам - готовность контрагента запустить у себя "прикольный" обфусцированный код считается за один из тестов для отчета =)


    1. alex0x08 Автор
      14.06.2024 21:18

      "Волков бояться - в лес не ходить" (ц), тут всего лишь 18 строк, причем чтобы это заработало еще нужны определенные внешние условия.

      При этом с каждым обновлением ОС или пакетов npm приезжает и выполняется автоматически столько всего интересного, что диву даешься.

      Так что боюсь подход "не запускать непонятное" давно не спасает от приключений.


  1. Penb69
    14.06.2024 21:18
    +1

    Чтобы вы не мучились с вводом кода с картинки, вот текстовая версия:

    А контрагент пусть страдает по полной, перепечатывая вот это с маленькой картонки?


  1. onets
    14.06.2024 21:18
    +4

    1. alex0x08 Автор
      14.06.2024 21:18

      Ух-ты! Еще не видел "железячные" реализации такой идеи, спасибо за линк.


  1. sekuzmin
    14.06.2024 21:18

    Криптография

    Когда я только начинал думать над реализацией этой штуки, уже было ясно что нужен какой-то неочевидный контроль целостности:

    Поэтому хотелось хоть какую-то защиту от подделки содержимого, чтобы компьютерные дети не добавили патч Брамина в самое интересное место, а индийский паренек не подменил авторство и мои контакты на свои, ради строчки в резюме.

    Криптография занимает почти половину скрипта, но не решает ни одной из обозначенных проблем. Изменить код и зашифровать тот-же текст сообщения можно, как и изменить текст сообщения не меняя код.


    Замените ED="TfPrCIlXEUInGJPpr4++hQfa2Whq4RFzdbFP5C4s/s8="; на ED="SnRi4cIB68E9aIh4TMqTDg==";


    CyberChef -> Base64 -> AES-ECB, Key=D5312E3E4203FA438E672C9CAF0A31A8, IV=0


  1. CitizenOfDreams
    14.06.2024 21:18
    +5

    "Здравствуйте, я молдавский веб-сервер. Пожалуйста, впечатайте этот код в ваш любимый редактор..."