Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "WinAPI";
Текущий архив: 2005.01.09;
Скачать: [xml.tar.bz2];

Вниз

NTFS - Процесс без файла - глюк или фантастика ?   Найти похожие ветки 

 
kaZaNoVa ©   (2004-11-20 15:38) [0]

Недавно мне попалась статья о том, что используя механизм файловых потоков NTFS можно реализовывать некоторые интересные идеи.
Например если запустить программу из файлового потока, то можно "файл-носитель" удалить (!), а программа будет далее работать !
- имхо один из самых лучших способов самоудаления для инсталляторов, а также _очень_ хороший способ защиты от изменений ЕХЕ
- для защиты коммерческого ПО - основной ЕХЕ можно хранить зашифрованным, и используя этот механизм возможно фактически "процесс без исполняемого файла !" - то есть кракер НЕ СМОЖЕТ исследовать ЕХЕ так,как ЕГО НЕТ НА диске !!!

мой главный вопрос - это глюк, связанный с недоработкой файловой системы NTFS или стандарная возможность системы ?

и еще, работает ли это на ХР SP2 ? (просьба протестировать код)
и будет ли работать в ледующих версиях системы ? (LongHorn ?)
(у меня на 2003 работает) ;)

а также - есть ли документация на файловые потоки NTFS ?

код:

program TestNTFS;

uses
 Windows,SysUtils;

const StreamName=":sys_file_system";

Var zapusk:integer;
my,target:string;

function CopyFileI(source, dest:pchar):boolean;
var h,h2:integer;
mas:array[1..2048] of byte;
i:cardinal;
done:cardinal;
begin
result:=false;  h:=0;  h2:=0;

Try
 h:=CreateFile(source,GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
 h2:=CreateFile(dest,GENERIC_WRITE,FILE_SHARE_WRITE,nil,OPEN_ALWAYS,0,0);
if (h2<=0) or (h<=0) then exit;

while ReadFile(h,mas,sizeof(mas),done,nil) and (done<>0) do
        WriteFile(h2,mas,done,i,nil);
Except End;

  CloseHandle(h);
   CloseHandle(h2);
result:=true;
end;

begin
if pos(StreamName,Paramstr(0))>0 then begin
MessageBox(0,"Ура, Мы свободны !!","Фантастика",mb_ok);
 MessageBox(0,"Мы работаем без файла !!!","Фантастика v2",mb_ok);
  Exit;
end;

my:=Paramstr(0);
target:=ChangeFileExt(my,".txt");

if CopyFileI(Pchar(my),Pchar(target+StreamName)) then
begin
zapusk:=WinExec(PChar(target+StreamName),SW_SHOWDEFAULT);
 if zapusk<32 then MessageBox(0,Pchar(SysErrorMessage(GetLastError)),Pchar("Ошибка # "+IntToStr(GetLastError)),0);
   Sleep(350);
     if not DeleteFile(Pchar(target)) then MessageBox(0,Pchar(SysErrorMessage(GetLastError)),Pchar("Ошибка # "+IntToStr(GetLastError)),0);
end;

end.


 
Игорь Шевченко ©   (2004-11-20 17:57) [1]

А файл-то при этом на диске есть ? Который .txt ?


 
Piter ©   (2004-11-20 18:14) [2]

kaZaNoVa ©   (20.11.04 15:38)
Например если запустить программу из файлового потока, то можно "файл-носитель" удалить (!), а программа будет далее


а чего ты так переживаешь? Насчет вот удалить не получится - но вот переместить EXE"шник (в том числе переименовать его, дать ему расширение TXT например) во время работы программы в NT системах можно (NTFS не при чем)


 
kaZaNoVa ©   (2004-11-20 18:19) [3]

Напоминаю смысл кода:

первоначально файла .ТХТ нет!
он создаётся на время работы, при этом программа стартует из него, и ЕХЕ таким образом свободен ;)
- то еть его (ехе можно удалить программой, запущенной из .тхт, которая уже работает без файла на диске !)

Piter ©   (20.11.04 18:14) [2]
Ты не совсем понял смысла кода - попробуй запустить, велезет мессага - её не закрывай, удали ехе - и фактически программа работает сама по себе :)))

-

> Насчет вот удалить не получится
а у меня - именно УДАЛИТЬ !!


 
kaZaNoVa ©   (2004-11-20 18:23) [4]

файл тхт тоже удаляется ;)


 
Игорь Шевченко ©   (2004-11-20 18:32) [5]

Я нифига не понял. Ты переписываешь свой exeшник в именованный поток текстового файла, запускаешь его, после этого удаляешь безымянный поток текстового файла, и что тут нового ?
Точно также ты можешь переписать exeшник в один файл, запустить его, удалить любой другой файл и твой процесс будет работать. Что нового ?


 
Piter ©   (2004-11-20 18:32) [6]

Хм, ну и что? Не понимаю восторгов. С таким же успехом можно создать удаленный поток, который убьет любой файл (в том числе и EXE), после чего завершится.


 
kaZaNoVa ©   (2004-11-20 18:38) [7]

имхо .. новое то, что фактически ехе работает без файла ;)

Piter ©   (20.11.04 18:32) [6]
я в соседней теме уже долго над этим бьюсь ..
пока выходин не очень грамотно ...


 
Игорь Шевченко ©   (2004-11-20 18:48) [8]


> имхо .. новое то, что фактически ехе работает без файла
> ;)


В этом ты здорово ошибаешься. Открой Process Explorer, найди свой процесс и посмотри, какие файлы им заняты.


 
Leonid Troyanovsky   (2004-11-20 19:09) [9]

Приветствую, Игорь.


> В этом ты здорово ошибаешься. Открой Process Explorer, найди
> свой процесс и посмотри, какие файлы им заняты.


Не заняты.
Т.е., исходный может удалить даже запущенная копия
(если, конечно обходится без WinExec)

--
С уважением, LVT.


 
Leonid Troyanovsky   (2004-11-20 19:15) [10]

Приветствую, kaZaNoVa.


> имхо .. новое то, что фактически ехе работает без файла
> ;)


Gary Nebbett когда-то показывал как запускать процесс
из памяти (NT4+).
В NTFS нет нужды, т.к. запуск идет, действительно,
из памяти.

--
С уважением, LVT.


 
Leonid Troyanovsky   (2004-11-20 19:19) [11]

Приветствую, Piter.


> Хм, ну и что? Не понимаю восторгов. С таким же успехом можно
> создать удаленный поток, который убьет любой файл (в том
> числе и EXE), после чего завершится.


Память, распределенную для потока в чужом процессе
сам поток освободить не сможет. Если, конечно,
не пользоваться трюками в духе того же Gary Nebbett,
выполняя код из стека.

--
С уважением, LVT.


 
Piter ©   (2004-11-20 19:32) [12]

Leonid Troyanovsky   (20.11.04 19:19) [11]
Память, распределенную для потока в чужом процессе
сам поток освободить не сможет


почему?!


 
Игорь Шевченко ©   (2004-11-20 19:34) [13]

Leonid Troyanovsky   (20.11.04 19:09) [9]

Приветствую, Леонид!

Файл не занят (в списке Handles его нет, он есть в списке DLL).
Тогда вопрос - насколько мне известно, CreateProcess в какой-то момент создает FileMapping для исполняемого образа, получается, что этого недостаточно для блокировки файла от удаления ?
Если файл можно удалить после запуска, то, получается, что выделенное под него место может быть теоретически использовано сразу же под другое содержимое. А как же тогда будет работать подкачка страниц из исполняемого образа ?

С уважением,
Игорь Шевченко

PS: Какими судьбами здесь ?


 
Leonid Troyanovsky   (2004-11-20 19:43) [14]


> Память, распределенную для потока в чужом процессе
> сам поток освободить не сможет

> почему?!


После VirtualFree ему надо еще нормально завершиться,
то есть выйти из функции потока.
А такие трюки, как исполнение кода из стека скоро
будут запрещены, ввиду великой опасности.

--
С уважением, LVT.


 
kaZaNoVa ©   (2004-11-20 19:44) [15]

Leonid Troyanovsky   (20.11.04 19:19) [11]
огромное спасибо за поддержку ;)

я тут уже давно интересуюсь "недокументированными возможностими системы" :))

по сабжу - я пока не смог открыть файл, который удалил ..:)

а из памяти - пока не знаю как ..

вариант с потоком - в другом процессе - без длл - наполовину уже реализован - в соседней ветке :)


 
Piter ©   (2004-11-20 19:50) [16]

Игорь Шевченко ©   (20.11.04 19:34) [13]
PS: Какими судьбами здесь ?


Леонид из ФИДО что ли? :)
Очень похоже, по крайней мере

И еще похоже - очень хороший спец, судя по реакции Игоря...


 
Leonid Troyanovsky   (2004-11-20 19:57) [17]

Приветствую, Игорь.


> Тогда вопрос - насколько мне известно, CreateProcess в какой-то
> момент создает FileMapping для исполняемого образа, получается,
> что этого недостаточно для блокировки файла от удаления
> ?


В принципе, недостаточно. В исполняемом файле есть
его хендл (проекции файла). Если его закрыть, то
файл можно и удалить. Раньше это был (NT4,5), AFAIK.
THandle(4), а потом (XP), для exe спрятали дальше.
Для dll он пока там же.

http://groups.google.com/groups?selm=c14f01c1a0f7%24f9b3f010%249ae62ecf%40tkmsftngxa02


> Если файл можно удалить после запуска, то, получается, что
> выделенное под него место может быть теоретически использовано
> сразу же под другое содержимое. А как же тогда будет работать
> подкачка страниц из исполняемого образа ?


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


> PS: Какими судьбами здесь ?


Да, так - мимо проходил :)

--
С уважением, LVT.


 
Leonid Troyanovsky   (2004-11-20 20:08) [18]

Приветствую.


> я тут уже давно интересуюсь "недокументированными возможностими
> системы" :))
>
> по сабжу - я пока не смог открыть файл, который удалил ..:)
>
> а из памяти - пока не знаю как ..


Ничего, если далее без поклонов? Я, понял, тут все по
домашнему, да и для оформления правилам не очень тут
приспособлено.

Gary Nebbett, как раз, целую книгу написал, типа справочника.
Можно найти и в электронном виде.

Насчет открытия удаленного файла я не очень понял.

А запуск из памяти делается так

http://groups.google.com/groups?selm=3e7f8d41%240%24227%24cc9e4d1f%40news.dial.pipex.com

Где-то я делал ему дельфийский перевод.


 
Leonid Troyanovsky   (2004-11-20 20:11) [19]


> Где-то я делал ему дельфийский перевод.


http://groups.google.com/groups?hl=ru&lr=&selm=1991108605%40f1003.n5080.z2.fidonet.ftn


 
Игорь Шевченко ©   (2004-11-20 20:29) [20]

Leonid Troyanovsky   (20.11.04 20:11) [19]


> > Где-то я делал ему дельфийский перевод.


Вопрос такой:

" ReadProcessMemory(pi.hProcess, PCHAR(context.ebx) + 8, @x, sizeof (x), nb);

 ZwUnmapViewOfSection(pi.hProcess, x);
..........
 q := VirtualAllocEx( pi.hProcess,
..........
 WriteProcessMemory(pi.hProcess, PCHAR(context.Ebx) + 8, @q, sizeof(q), nb);"

Насколько я понимаю, адрес проекции заменяется на адрес полученный непосредственным выделением памяти.
Когда процесс завершается, IMHO, где в недрах завершателя должен быть вызов UnmapViewOfSection, при этом адрес памяти на проекцю не ссылается.
Проблем из-за этого не возникнет ?

С уважением,
Игорь Шевченко


 
Leonid Troyanovsky   (2004-11-20 20:45) [21]


> kaZaNoVa ©   (20.11.04 19:44) [15]
> вариант с потоком - в другом процессе - без длл - наполовину
> уже реализован - в соседней ветке :)


Для удаления экзешников можно воспользоваться Explorer.
Правда, совсем без dll неудобно, т.к. проще всего это
сделать устанавливая хук на WH_GETMESSAGE.
Послав окну проводника условленное сообщение (передавая
ему hhook и значение хендла с проекцией файла от  
DuplicateHandle) , после обработки этого сообщения в хуковой
процедуре хук можно снять (и далее библиотека уже не нужна).
Сама обработка заключается в открытии проекции и выполнении
кода потока, записанного туда (вместе с FileName).
Сам код потока д.б. перемещаемым, т.е., обращаться только
к функциям kernel32 (обычно грузимым по одному адресу)
и переменным из стека и (возможно из проекции файла).
По сути, поток дожидается окончаний процесса WaitForSingleObject
делает DeleteFile и завершается. Хотя поток и сможет
сделать CloseHandle проекции файла, однако UnMapViewOfFile
ему сделать не удасться (вот они - утечки).

Т.е., библиотека, все же, пригодится и во второй раз -
для корректного завершения потока (в обратном порядке,
в ответ на еще одно сообщение).

Ну, а, вообще, такие схемы напоминают вредоносный код :)


 
kaZaNoVa ©   (2004-11-20 20:48) [22]

Leonid Troyanovsky   (20.11.04 20:45) [21]

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

для меня - самое сложное ...


> Ну, а, вообще, такие схемы напоминают вредоносный код
> :)
ага, все, что идёт глубже, чем обычный пользовательский уровень - в недра системы .. - можно назвать "хаком", однако, такие схемы позволяют то, что невозможно обычными способами .. а это здорово !!


 
Leonid Troyanovsky   (2004-11-20 21:11) [23]


> Игорь Шевченко ©   (20.11.04 20:29) [20]

> Насколько я понимаю, адрес проекции заменяется на адрес
> полученный непосредственным выделением памяти.
> Когда процесс завершается, IMHO, где в недрах завершателя
> должен быть вызов UnmapViewOfSection, при этом адрес памяти
> на проекцю не ссылается.
> Проблем из-за этого не возникнет ?


Насколько я представляю, поведение  UnmapViewOfSection
аналогично UnMapViewOfFile и некорректный адрес смутить ее
не может. А если что-то и осталось замапленным, то система,
все равно, гарантирует очистку.
Вспомним, хотя бы, TerminateProcess, который может случится
даже на самых финальных стадиях.

Во всяком случае, утечек системных ресурсов от этого кода
мне наблюдать не удалось (NT4, w2k3).


 
Leonid Troyanovsky   (2004-11-20 21:44) [24]



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

> для меня - самое сложное ...


library lib0;

uses
 Messages,
 Windows,
 injdef;

function ThreadProc(Info: PRemoteInfo): DWord; stdcall;
begin

 Result := 0;

 if Info.Event <> 0 then
   begin
     Info.pSetEvent(Info.Event);
     Info.pCloseHandle(Info.Event);
   end;

  if Info.hProcess <> 0 then
    begin
      Info.pWaitForSingleObject(Info.hProcess, INFINITE);
      Info.pCloseHandle(Info.hProcess);
    end;

   Info.pDeleteFile(Info.FileName);

 if Info.FileMap <> 0 then
    Info.pCloseHandle(Info.FileMap);
 
   Info.pExitThread(0);

end;

function DoThread( nCode: Integer; wprm: WParam; lprm:WParam):Lparam;
        stdcall;
type
 PMsg = ^TMsg;
var
 HFileMap : THandle;
 msg : PMsg;
 Info : PRemoteInfo;
 hThread : THandle;
 tid : DWord;
 HKernel : THandle;
 mmfname : String;
begin
 Result := 0;
 msg := PMsg(lprm);
 if (msg.Message = 0) and (msg.WParam = 1) then
   begin
     Str(msg.LParam, mmfname);
     mmfname := "_filemap"+ mmfname;
     HFileMap:=CreateFileMapping( $FFFFFFFF,
                                  nil,
                                  PAGE_READWRITE,
                                  0,
                                  $FFF,
                                  PChar(mmfname));
     if HFileMap = 0 then
       Exit;

     Info := MapViewOfFile( HFileMap,
                            FILE_MAP_WRITE,
                            0,
                            0,
                            0);
     if (Info = nil) then
       begin
         CloseHandle(HFileMap);
         Exit;
       end;

     Info.FileMap := HFileMap;

     CopyMemory( @Info.InjectThreadCode[0],
                 @ThreadProc,
                 Longint(@DoThread)- Longint(@THreadProc));

     HKernel := GetModuleHandle("Kernel32.dll");
     Info.pSetEvent:= GetProcAddress(HKernel,"SetEvent");
     Info.pCloseHandle := GetProcAddress(HKernel,"CloseHandle");
     Info.pWaitForSingleObject:=GetProcAddress(HKernel,"WaitForSingleObject");
     Info.pDeleteFile := GetProcAddress(HKernel, "DeleteFileA");
     Info.pExitThread := GetProcAddress(HKernel, "ExitThread");

     FlushViewOfFile(Info, 0);

     hThread := CreateThread( nil,
                              1024,
                              @Info.InjectThreadCode[0],
                              Info,
                              0,
                              tid);
     if hThread <> 0 then
        CloseHandle(hThread);
   end;
end;

exports
 DoThread;

begin
end.

----
unit injdef;

interface

uses
 Windows;

type
   FDeleteFile = function (lpFilename: PChar): LongBool;stdcall;
   FSetEvent = function (aHandle: THandle): LongBool;stdcall;
   FCloseHandle = function(aHandle: THandle): LongBool;stdcall;
   FWaitForSingleObject = function (aHandle: THandle; dwTime: Dword): Dword;stdcall;
   FExitThread = procedure (ExitCode: DWord);stdcall;

  TRemoteInfo = packed record
    FileMap : THandle;
    Event : THandle;
    hProcess: THandle;
    pSetEvent: FSetEvent;
    pDeleteFile: FDeleteFile;
    pCloseHandle : FCloseHandle;
    pWaitForSingleObject: FWaitForSingleObject;
    pExitThread : FExitThread;
    FileName : array [0..MAX_PATH] of Char;
    InjectThreadCode : Array [0..1023] of Byte;
  end;

  PRemoteInfo= ^TRemoteInfo;

implementation

end.

---
program ad2;

{$APPTYPE CONSOLE}

uses
 Windows,
 SysUtils,
 injdef;

const
 {Warning: use other target instead Explorer}
 TargetWindowClass : PChar = "Progman";

var
 tid : THandle;
 AHook: THandle;
 Event : THandle;
 HookDll : THandle;
 HFileMap:THandle;
 Info : PRemoteInfo;

function DupHandle(RemoteProcess: DWord; Handle: THandle): THandle;
begin
 Result := 0;
 Win32Check(DuplicateHandle( GetCurrentProcess,
                  Handle,
                  RemoteProcess,
                  @Result,
                  0,
                  False,
                  DUPLICATE_SAME_ACCESS));
end;

procedure Inject(Target: THandle);
var
 aproc : Pointer;
 rpid : DWord;
 hRProcess: THandle;

begin
 HookDll:= LoadLibrary("lib0.dll");
 Win32Check(HookDll> 0);

 aproc := GetProcAddress(HookDll, "DoThread");
 Win32Check(aproc <> nil);

 tid := GetWindowThreadProcessId(Target, @rpid);
 Win32Check(tid <> 0);

 hRProcess := OpenProcess(PROCESS_ALL_ACCESS, False, rpid);
 Win32Check(hRProcess <> 0);

 Event:= CreateEvent(nil, True, False, nil);

 HFileMap:=CreateFileMapping( $FFFFFFFF,
                              nil,
                              PAGE_READWRITE,
                              0,
                              $FFF,
                              PChar("_filemap"+IntToStr(GetCurrentProcessID)));

 Win32Check(HFileMap <> INVALID_HANDLE_VALUE);

 Info := MapViewOfFile( HFileMap,
                      FILE_MAP_WRITE,
                      0,
                      0,
                      0);

 Win32Check(Info <> nil);

 Info.Event := DupHandle(hRProcess, Event);
 Info.hProcess := DupHandle(hRProcess, GetCurrentProcess);

 if hRProcess <> 0 then
   CloseHandle(hRProcess);

 CopyMemory(@Info.FileName[0], PChar(ParamStr(0)), Length(ParamStr(0))+1);
 FlushViewOfFile(Info, 0);

 AHook := SetWindowsHookEx(WH_GETMESSAGE, aproc, HookDll, tID);
 Win32Check(AHook <> 0);

 PostThreadMessage(tid, 0, 1, GetCurrentProcessID);
 Sleep(0);
end;

procedure Reject;
begin
 if Info <> nil then
   UnmapViewOfFile(Info);
 if HFileMap <> 0 then
   CloseHandle(HFileMap);

 if AHook <> 0 then
   begin
     UnHookWindowsHookEx(AHook);
     PostThreadMessage(tid, 0, 0, 0);
   end;

 if HookDll <> 0 then
   FreeLibrary(HookDll);

 if Event <> 0 then
   CloseHandle(Event);
end;

var
 hwnd : THandle;

begin
 hwnd := FindWindow(TargetWindowClass, nil);
 Win32Check(hwnd <> 0);

 try
   Inject(hwnd);
   case WaitForSingleObject(Event, 1000) of
     WAIT_OBJECT_0: ;
   else
     raise Exception.Create("Unable load dll")
   end;
 finally
   Reject;
 end;

 writeln("Press Enter for delete this module..");
 readln;
end.


 
Leonid Troyanovsky   (2004-11-20 21:54) [25]


> kaZaNoVa ©   (20.11.04 20:48) [22]

> > Ну, а, вообще, такие схемы напоминают вредоносный код :)

> ага, все, что идёт глубже, чем обычный пользовательский
> уровень - в недра системы .. - можно назвать "хаком", однако,
> такие схемы позволяют то, что невозможно обычными способами
> .. а это здорово !!


В данном случае, т.е. поток в чужом процессе - это недра
не системы, а лишь чужого процесса.
Ну, а позволяют оные схемы лишь то, что позволено.
Хотя, конечно, и дыры встречаются. Чего стоило одно лишь
сообщение WM_TIMER, исполнявшее код в чужом процессе :)


 
kaZaNoVa ©   (2004-11-20 21:58) [26]

Leonid Troyanovsky   (20.11.04 21:54) [25]
спасибо за пример - буду разбираться ;))


> Ну, а позволяют оные схемы лишь то, что позволено.

имхо много позволяют :)))
особенно если пользователь с правами админа :))


 
Leonid Troyanovsky   (2004-11-20 22:13) [27]


> kaZaNoVa ©   (20.11.04 21:58) [26]

> > Ну, а позволяют оные схемы лишь то, что позволено.

> имхо много позволяют :)))
> особенно если пользователь с правами админа :))


Это палка о двух концах. Жизнь подсказывает,
что даже если есть возможность использовать чрезвычайные
полномочия, делать это надо весьма дозировано.
По-крайней мере, не применять их при написании приложений ;)



Страницы: 1 вся ветка

Форум: "WinAPI";
Текущий архив: 2005.01.09;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.57 MB
Время: 0.036 c
1-1103759458
casper24
2004-12-23 02:50
2005.01.09
Электронно-Цифровая подпись... RSA/MD5...


6-1098265522
Кирилл.
2004-10-20 13:45
2005.01.09
как через прокси пингануть ност?


1-1103827386
Alexander666
2004-12-23 21:43
2005.01.09
New против GetMem и Dispose против FreeMem


4-1100790422
integer
2004-11-18 18:07
2005.01.09
Подскажите как найти в какой *.dll сидят свойства притера


14-1103203292
Игорь Шевченко
2004-12-16 16:21
2005.01.09
Министр обороны призвал "прекратить дебилизацию" россиян





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский