Сейчас на форуме: rmn, exp50848 (+7 невидимых)

 eXeL@B —› Основной форум —› Вопрос по CryptoAPI
<< . 1 . 2 .
Посл.ответ Сообщение


Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 20 августа 2013 21:05 · Поправил: ARCHANGEL
· Личное сообщение · #1

Добрый вечер, уважаемые. Ковыряю одну программку, всё мне, в общих чертах, с ней понятно. Но есть один интересный момент. Попробую его подробно описать.

Дело в том, что программка эта использует CryptoAPI в ходе проверки регистрационного ключа. Если описывать процесс подробно, то всё обстоит примерно так. Вначале программа вычисляет hwid по параметру даты биоса из реестра и GetVolumeInformation, это неважно. Далее эти параметры она, типа, хэширует по какому-то самопальному алгоритму, тоже неважно. Потом начинается самое интересное.

Вначале создаётся объект для работы с md5 хэшем:

Code:
  1.  lea     ecx, [ebp+hHash]                ; Load Effective Address
  2. push    ecx                             ; phHash
  3.  push    0                               ; dwFlags
  4.  push    0                               ; hKey
  5.  push    CALG_MD5                        ; Algid
  6.  push    [ebp+hProv]                     ; hProv
  7.  call    CryptCreateHash            


Потом, естественно, идёт вычисление хэша md5 от слабого (самопального) хэша.

Code:
  1. push    0                               ; dwFlags
  2. push    [ebp+pbData]                    ; s
  3. call    _strlen                         ; Call Procedure
  4. pop     ecx
  5. push    eax                             ; dwDataLen
  6. push    [ebp+pbData]                    ; pbData
  7. push    [ebp+hHash]                     ; hHash
  8. call    CryptHashData              


Далее, как я понимаю, на основе md5-хэша по алгоритму rc2 создаётся сессионный ключ:

Code:
  1. lea     ecx, [ebp+hKey]                 ; Load Effective Address
  2. push    ecx                             ; phKey
  3. push    0                               ; dwFlags
  4. push    [ebp+hHash]                     ; hBaseData
  5. push    CALG_RC2                        ; Algid
  6. push    [ebp+hProv]                     ; hProv
  7. call    CryptDeriveKey             


Теперь вспомним про то, что мы ведь тоже ввели какой-то серийник, который, собственно и проверяется в ходе регистрации. Так вот, в ходе проверки из этого серийника всегда получается массив длиной в восемь байт. Всегда! Алгоритм проверки реализован без косяков, т.е. на вычиисление длины не влияет, есть ли внутри массива нулевые байты или нет - всё работает, как часы. Ок, идём дальше.

Сейчас у нас есть сессионный ключ, и есть 8-ми байтовый массив. Далее происходит расшифровка массива по сессионному ключу:

Code:
  1.  lea     edx, [ebp+pdwDataLen]           ; Load Effective Address
  2.  push    edx                             ; pdwDataLen
  3.  push    [ebp+var_34]                    ; pbData
  4.  push    0                               ; dwFlags
  5.  push    TRUE                            ; Final
  6.  push    0                               ; hHash
  7.  push    [ebp+hKey]                      ; hKey
  8.  call    CryptDecrypt               


И вот здесь - самое вкусное. Расшифровка-то происходит (на месте старых 8-ми байт появляются новые значения), но вот функция CryptDecrypt возвращает FALSE, GetLastError() возвращает NTE_BAD_DATA. Я тут гуглил-гуглил, и нагуглил --> Это <--. Там в комментах кто-то пишет такое:

The typical reason why CryptDecrypt would fail with NTE_BAD_DATA would be that you're decrypting the last block of a block cypher (as you are) and the decrypted padding bytes are incorrect. This can happen if the input is truncated (not all encrypted bytes were saved to the file) or if the key is incorrect.

В общем, если то, что написано, правда, то тут ситуация ясна - key is incorrect стопудово, т.к. откуда ж мне знать его правильный. В связи с этим возникает несколько вопросов.

1. Правда ли, что при неправльном сессионном ключе может возникать такая ошибка? (А то я просто думал, что если ключ для алгоритма расшифровки неверный, то просто будут данные некорректные, но в ходе расшифровки это никак не спалить, это ж не цифровая подпись)
2. Если правда, то как выполнить атаку, чтоб найти правильный?

Спасибо всем, кто осилил сей пост до конца и проникся моими проблемами. Саму программу я не прикладываю, ибо большая, узкоспециализированная и никому сама по себе неинтересна.

Примечание

int, я надеюсь, что &nbsp; снятся тебе каждую ночь.

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 337.6 (мудрец), 224thx
Активность: 0.210.1
Статус: Участник
born to be evil

Создано: 21 августа 2013 14:34 · Поправил: ajax
· Личное сообщение · #2

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

-----
От многой мудрости много скорби, и умножающий знание умножает печаль




Ранг: 590.4 (!), 408thx
Активность: 0.360.18
Статус: Модератор

Создано: 21 августа 2013 14:37 · Поправил: r_e
· Личное сообщение · #3

Это не гон, это соль называется (salt). Для предотвращения определенного типа атак в голову дописывается рандомный блок данных фиксированной длины. Во время декрипта он просто обрезается.
Он может быть и переменной длины - тут все решает реализация.

-----
старый пень


| Сообщение посчитали полезным: ajax


Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 21 августа 2013 14:40
· Личное сообщение · #4

r_e
Не, ну так данные ж от этого не страдают!

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 337.6 (мудрец), 224thx
Активность: 0.210.1
Статус: Участник
born to be evil

Создано: 21 августа 2013 14:43
· Личное сообщение · #5

ARCHANGEL
оригинальные данные - нет. криптованые - да. что и пытаемся донести

-----
От многой мудрости много скорби, и умножающий знание умножает печаль





Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 17 сентября 2013 17:20
· Личное сообщение · #6

Возник вопрос, связанный с криптографией, решил не плодить темы - спросить здесь. В общем, есть у меня программа, она использует AES в своей рег. схеме. Вначале она создаёт буфер длиной 16 байт, заполняет его нулями. Потом выполняется, как я понял, Encription, и в буфере появляются байтики:

Code:
  1. 04 4B FE E7 33 5E 6A 34  06 0A 4C EF D3 E5 E2 9D


Я стал читать описание алгоритма, и столкнулся с несколькими непонятными мне моментами. Вначале я нашёл реализацию на паскале (!): --> Pascal AES <--

Потом стал искать классы в дотнете, и нашёл ссылку: --> AES .Net <--

В дотнете упоминается так называемый "вектор инициализации", который --> Вот <--

В паскалевой реализации я его найти не смог. Потом прочитал статью на хабре: --> Статья <--, из которой понял, что у меня используется ECB без паддинга. Размер блока - 128 бит. Теперь вопрос: не могли бы знающие люди на паскалевом сорце (т.к. реализация очень на него похожа) показать, где там определяется размер ключа, и где этот вектор инициализации, ну, а дальше я уж сам. Или, возможно, есть какие-то заманухи без вектора инициализации?

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 1053.6 (!!!!), 1078thx
Активность: 1.060.81
Статус: Участник

Создано: 17 сентября 2013 17:25 · Поправил: reversecode
· Личное сообщение · #7

вектор используется в CBC, в паскале он InitVector




Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 17 сентября 2013 17:42
· Личное сообщение · #8

reversecode
Сейчас осмотрел паскалевый исходник на предмет наличия InitVector, и получается, что он используется только при поточном шифровании/дешифровке, и при блочном - не используется.

Code:
  1. // Block encryption routines
  2.  
  3. procedure EncryptAES(const InBuf: TAESBuffer; const Key: TAESExpandedKey128;
  4.   var OutBuf: TAESBuffer); overload;
  5. procedure EncryptAES(const InBuf: TAESBuffer; const Key: TAESExpandedKey192;
  6.   var OutBuf: TAESBuffer); overload;
  7. procedure EncryptAES(const InBuf: TAESBuffer; const Key: TAESExpandedKey256;
  8.   var OutBuf: TAESBuffer); overload;
  9.  
  10. // Stream encryption routines (ECB mode)
  11.  
  12. procedure EncryptAESStreamECB(Source: TStream; Count: cardinal;
  13.   const Key: TAESKey128; Dest: TStream); overload;
  14. procedure EncryptAESStreamECB(Source: TStream; Count: cardinal;
  15.   const ExpandedKey: TAESExpandedKey128; Dest: TStream); overload;
  16.  
  17. procedure EncryptAESStreamECB(Source: TStream; Count: cardinal;
  18.   const Key: TAESKey192; Dest: TStream); overload;
  19. procedure EncryptAESStreamECB(Source: TStream; Count: cardinal;
  20.   const ExpandedKey: TAESExpandedKey192; Dest: TStream); overload;
  21.  
  22. procedure EncryptAESStreamECB(Source: TStream; Count: cardinal;
  23.   const Key: TAESKey256; Dest: TStream); overload;
  24. procedure EncryptAESStreamECB(Source: TStream; Count: cardinal;
  25.   const ExpandedKey: TAESExpandedKey256; Dest: TStream); overload;


Т.е. получается, что я неправильно выразился. У меня в программке тупо шифруется блок. Но риппать код не хочется, есть ли в С# блочное шифрование, аналогичное паскалевому?

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 1053.6 (!!!!), 1078thx
Активность: 1.060.81
Статус: Участник

Создано: 17 сентября 2013 17:50 · Поправил: reversecode
· Личное сообщение · #9

ну наверное, кто из нас на C# пишет?))
--> Link <--
--> Link <--




Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 17 сентября 2013 18:21 · Поправил: ARCHANGEL
· Личное сообщение · #10

Форум не даёт длинные аттачи делать ((

Ладно, выделим главное:

Code:
  1.   v55 = *(_DWORD *)(a1 + 20) ^ *(_DWORD *)a2;
  2.   v56 = *(_DWORD *)(a1 + 24) ^ *(_DWORD *)(a2 + 4);
  3.   v57 = *(_DWORD *)(a1 + 28) ^ *(_DWORD *)(a2 + 8);
  4.   v58 = *(_DWORD *)(a1 + 32) ^ *(_DWORD *)(a2 + 12);
  5.   v3 = a1 + 36;
  6.   if ( *(_DWORD *)(a1 + 16) > 6u )
  7.   {
  8.     v4 = *(_DWORD *)v3 ^ dword_D5F5B4[(unsigned __int16)(HIWORD(v58) >> 16) + 768] ^ dword_D5F5B4[(((unsigned int)v57 >> 16) & 0xFF)
  9.                                                                                                 + 512] ^ dword_D5F5B4[BYTE1(v56) + 256] ^ dword_D5F5B4[(unsigned __int8)v55];
  10.     v5 = *(_DWORD *)(a1 + 40) ^ dword_D5F5B4[(unsigned __int16)(HIWORD(v55) >> 16) + 768] ^ dword_D5F5B4[(((unsigned int)v58 >> 16) & 0xFF) + 512] ^ dword_D5F5B4[BYTE1(v57) + 256] ^ dword_D5F5B4[(unsigned __int8)v56];
  11.     v6 = *(_DWORD *)(a1 + 44) ^ dword_D5F5B4[(unsigned __int16)(HIWORD(v56) >> 16) + 768] ^ dword_D5F5B4[(((unsigned int)v55 >> 16) & 0xFF) + 512] ^ dword_D5F5B4[BYTE1(v58) + 256] ^ dword_D5F5B4[(unsigned __int8)v57];
  12.     v7 = *(_DWORD *)(a1 + 48) ^ dword_D5F5B4[(unsigned __int16)(HIWORD(v57) >> 16) + 768] ^ dword_D5F5B4[(((unsigned int)v56 >> 16) & 0xFF) + 512] ^ dword_D5F5B4[BYTE1(v55) + 256] ^ dword_D5F5B4[(unsigned __int8)v58];
  13.     v55 = *(_DWORD *)(a1 + 52) ^ dword_D5F5B4[(unsigned __int16)(HIWORD(v7) >> 16) + 768] ^ dword_D5F5B4[(((unsigned int)v6 >> 16) & 0xFF) + 512] ^ dword_D5F5B4[BYTE1(v5) + 256] ^ dword_D5F5B4[(unsigned __int8)v4];
  14.     v56 = *(_DWORD *)(a1 + 56) ^ dword_D5F5B4[(unsigned __int16)(HIWORD(v4) >> 16) + 768] ^ dword_D5F5B4[(((unsigned int)v7 >> 16) & 0xFF) + 512] ^ dword_D5F5B4[BYTE1(v6) + 256] ^ dword_D5F5B4[(unsigned __int8)v5];
  15.     v57 = *(_DWORD *)(a1 + 60) ^ dword_D5F5B4[(unsigned __int16)(HIWORD(v5) >> 16) + 768] ^ dword_D5F5B4[(((unsigned int)v4 >> 16) & 0xFF) + 512] ^ dword_D5F5B4[BYTE1(v7) + 256] ^ dword_D5F5B4[(unsigned __int8)v6];
  16.     v3 = a1 + 68;
  17.     v58 = *(_DWORD *)(a1 + 64) ^ dword_D5F5B4[(unsigned __int16)(HIWORD(v6) >> 16) + 768] ^ dword_D5F5B4[(((unsigned int)v5 >> 16) & 0xFF) + 512] ^ dword_D5F5B4[BYTE1(v4) + 256] ^ dword_D5F5B4[(unsigned __int8)v7];
  18.   }


Это в коде моей прожки подопытной. В паскале вообще вот так:

Code:
  1.   // last round of transformations
  2.   W0 := LastForwardTable[Byte(T1[0])]; W1 := LastForwardTable[Byte(T1[1] shr 8)];
  3.   W2 := LastForwardTable[Byte(T1[2] shr 16)]; W3 := LastForwardTable[Byte(T1[3] shr 24)];
  4.   T0[0] := (W0 xor ((W1 shl 8) or (W1 shr 24)) xor ((W2 shl 16) or (W2 shr 16))
  5.     xor ((W3 shl 24) or (W3 shr 8))) xor Key[48];
  6.   W0 := LastForwardTable[Byte(T1[1])]; W1 := LastForwardTable[Byte(T1[2] shr 8)];
  7.   W2 := LastForwardTable[Byte(T1[3] shr 16)]; W3 := LastForwardTable[Byte(T1[0] shr 24)];
  8.   T0[1] := (W0 xor ((W1 shl 8) or (W1 shr 24)) xor ((W2 shl 16) or (W2 shr 16))
  9.     xor ((W3 shl 24) or (W3 shr 8))) xor Key[49];
  10.   W0 := LastForwardTable[Byte(T1[2])]; W1 := LastForwardTable[Byte(T1[3] shr 8)];
  11.   W2 := LastForwardTable[Byte(T1[0] shr 16)]; W3 := LastForwardTable[Byte(T1[1] shr 24)];
  12.   T0[2] := (W0 xor ((W1 shl 8) or (W1 shr 24)) xor ((W2 shl 16) or (W2 shr 16))
  13.     xor ((W3 shl 24) or (W3 shr 8))) xor Key[50];
  14.   W0 := LastForwardTable[Byte(T1[3])]; W1 := LastForwardTable[Byte(T1[0] shr 8)];
  15.   W2 := LastForwardTable[Byte(T1[1] shr 16)]; W3 := LastForwardTable[Byte(T1[2] shr 24)];
  16.   T0[3] := (W0 xor ((W1 shl 8) or (W1 shr 24)) xor ((W2 shl 16) or (W2 shr 16))
  17.     xor ((W3 shl 24) or (W3 shr 8))) xor Key[51];


На последнем раунде Key[51] используется, это что получается - там 148h байт в ключе? Как так?

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 1053.6 (!!!!), 1078thx
Активность: 1.060.81
Статус: Участник

Создано: 17 сентября 2013 18:27 · Поправил: reversecode
· Личное сообщение · #11

чет я запутался,паскаль асм си сишарп
че делаем то?
на асме выдержка из универсального AES, длинну ключа правильно определил в оригинале на асме?
паскакаль то причем здесь? и у паскакаля там три реализации на три длинны ключа, ты точно правильную выбрал?

pastenbin есть для таких аттачей




Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 17 сентября 2013 18:47
· Личное сообщение · #12

reversecode
В общем, ещё раз. Есть прожка, в ней есть алго, я его анализировал и пришёл к выводу, что это - AES. В прожке юзаются ForwardTable и LastForwardTable, точно такие же, как и в паскалевом исходнике. Собственно, по ним я исходник и нашёл. Мне для кейгена нужно воспроизвести такой же процесс шифрования, точно такой же. На хочу не рипать асм-код и не кодить кейген на С++, а взять шарп. В ольке при трейсинге подсвечивается изменение буфера при его шифровании, т.е. был пустой буфер (16 байт забитые нулями), после шифрования получилось значение, которое я выше уже приводил:

Code:
  1. 04 4B FE E7 33 5E 6A 34  06 0A 4C EF D3 E5 E2 9D


Отсюда я делаю вывод, что реализация работает с блоками 16 байт, т.е. 128 бит. Никаких других таблиц, кроме ForwardTable и LastForwardTable, я не видел. Значит не юзается никакой вектор инициализации. Паддинга тоже нет - тупо взяли блок в 16 байт и шифранули. Вот я и хочу понять, каким способом.

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 1053.6 (!!!!), 1078thx
Активность: 1.060.81
Статус: Участник

Создано: 17 сентября 2013 18:54 · Поправил: reversecode
· Личное сообщение · #13

if ( *(_DWORD *)(a1 + 16) > 6u )
брякнись сюда и посмотри длинну ключа, с раундов переведешь в длинну, это будет надежней что бы определить какой алго юзается
а то малоли что там в буффере

и да, последний писк моды это каждый юзвер патчит свой AES ))
так что малоли,
снимай дампы смотри вход выход на одном блоке, и сравнивай у себя а еще лучше на чем то стандартном,
онлайн AES декрипто-крипторов много
--> Link <--

| Сообщение посчитали полезным: ARCHANGEL


Ранг: 337.6 (мудрец), 224thx
Активность: 0.210.1
Статус: Участник
born to be evil

Создано: 17 сентября 2013 22:30
· Личное сообщение · #14

reversecode пишет:
и да, последний писк моды это каждый юзвер патчит свой AES

да, чаще проще рипать

-----
От многой мудрости много скорби, и умножающий знание умножает печаль





Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 17 сентября 2013 22:50
· Личное сообщение · #15

ajax
Тут другая проблема, ну, допустим, рипну я encrypt, а декрипта в прожке нет. Его тогда вручную писать надо, анализируя текущую реализацию.

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 18 сентября 2013 00:17
· Личное сообщение · #16

reversecode
Я там брякнулся, там 8.

с раундов переведешь в длинну

Вот как бы это сделать?

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 1053.6 (!!!!), 1078thx
Активность: 1.060.81
Статус: Участник

Создано: 18 сентября 2013 00:32
· Личное сообщение · #17

очень похоже на aes256, для надежности лучше конечно вручную проверять количество блоков,
а то малоли какая там реализация aes

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

ну либо наоборот, брякаешься на входе в свой aes, и в ольке забиваешь ключ и блок, и отпускаешь ольку до возврата, ну и опять же сравниваешь




Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 18 сентября 2013 00:48
· Личное сообщение · #18

reversecode

Да, абсолютно верно!


-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 1053.6 (!!!!), 1078thx
Активность: 1.060.81
Статус: Участник

Создано: 18 сентября 2013 00:51 · Поправил: reversecode
· Личное сообщение · #19

а тот что в паскале не получался какой aes был?

ARCHANGEL пишет:
Я на глаз сравнивал






Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 18 сентября 2013 00:58
· Личное сообщение · #20

reversecode
Так а кто ж паскаль компилил? Я на глаз сравнивал

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 18 сентября 2013 17:11
· Личное сообщение · #21

--> AES C# реализация <--

Вот это подходит для меня, буду использовать.

-----
Stuck to the plan, always think that we would stand up, never ran.





Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 02 октября 2013 13:55 · Поправил: ARCHANGEL
· Личное сообщение · #22

Тяжёлые будни криптографии опять наступили для меня. В этот раз читаю теорию, вот, всё хорошо и интересно, но есть одна непонятная вещь. Читаю я разную литературу, и в ней упоминается несколько разных операций - вычитание по модулю, умножение по модулю. Наряду с этим есть просто вычисление по модулю, в гугле нашёл ещё и --> Вычисление квадратных корней по модулю <--. Просто вычисление по модулю --> ЗДЕСЬ <-- трактовали, как получение остатка при делении одного числа на другое. Так ли это, и что из себя представляют другие операции?

-----
Stuck to the plan, always think that we would stand up, never ran.




Ранг: 590.4 (!), 408thx
Активность: 0.360.18
Статус: Модератор

Создано: 02 октября 2013 14:09 · Поправил: r_e
· Личное сообщение · #23

Если на пальцах, то вычисление по модулю - это общее название операций для которых характерно
A mod N = X, until X < N, X -= N; A, N, X принадлежат заданному полю.
Соответственно, остальные операции определяются классически с учетом правила выше.

Например:
Вычитание. A - B mod N = X, if X < 0 X += N, в поле натуральных с нулем.
Квадратный корень: sqrt(A) mod N = X, такое что X^2 mod N = A.

-----
старый пень


| Сообщение посчитали полезным: ARCHANGEL


Ранг: 681.5 (! !), 405thx
Активность: 0.420.21
Статус: Участник
ALIEN Hack Team

Создано: 02 октября 2013 14:32
· Личное сообщение · #24

r_e
Т.е. если я правильно понял, то:
1. Сложить числа 5 и 2 по модулю 3 - это (5 + 2) mod 3 = 1
2. Получить разность чисел 5 и 2 по модулю 3 - это (5 - 2) mod 3 = 0
и т.д.

-----
Stuck to the plan, always think that we would stand up, never ran.




Ранг: 590.4 (!), 408thx
Активность: 0.360.18
Статус: Модератор

Создано: 02 октября 2013 14:54
· Личное сообщение · #25

ARCHANGEL
Ну да, в поле N0 так и будет.

-----
старый пень



<< . 1 . 2 .
 eXeL@B —› Основной форум —› Вопрос по CryptoAPI
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати