Ллирик
|
|
« : 20-09-2016 18:57 » |
|
Почему такой код PDEVICE_OBJECT KeyDeviceObject;
NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { UNICODE_STRING QgusDeviceName; NTSTATUS status;
RtlInitUnicodeString(&QgusDeviceName, L"\\Device\\KeyMouse"); status = IoCreateDevice(DriverObject, 0, &QgusDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &KeyDeviceObject); ........................................................................................... на Win10x64 приводит к SYSTEM_THREAD_EXCEPTION_NOT_HANDLE и откуда в моём драйвере взялся код .text:0000000000012A70 __security_check_cookie proc near ; CODE XREF: __GSHandlerCheckCommon+5Ej .text:0000000000012A70 ; DriverEntry+2EFp .text:0000000000012A70 cmp rcx, cs:__security_cookie .text:0000000000012A77 jnz short ReportFailure .text:0000000000012A79 rol rcx, 10h .text:0000000000012A7D test cx, 0FFFFh .text:0000000000012A82 jnz short RestoreRcx .text:0000000000012A84 retn 0 .text:0000000000012A87 ; --------------------------------------------------------------------------- .text:0000000000012A87 .text:0000000000012A87 RestoreRcx: ; CODE XREF: __security_check_cookie+12j .text:0000000000012A87 ror rcx, 10h ; StackCookie .text:0000000000012A8B .text:0000000000012A8B ReportFailure: ; CODE XREF: __security_check_cookie+7j .text:0000000000012A8B jmp __report_gsfailure .text:0000000000012A8B __security_check_cookie endp ? Он тоже приводит к SYSTEM_THREAD_EXCEPTION_NOT_HANDLE. В моём исходнике его нет.
|
|
|
Записан
|
|
|
|
|
|
Ochkarik
|
|
« Ответ #3 : 21-09-2016 16:20 » |
|
либа BufferOverflowK.lib - подключена, не перепутали? или отключите эту проверку - самое простое)
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #4 : 21-09-2016 18:16 » |
|
а как её отключить в wdk? строка USER_C_FLAGS=/FAs /GS- в файле sources не помогает.
|
|
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #5 : 22-09-2016 11:17 » |
|
лучше вот так конечно. "USER_C_FLAGS=$(USER_C_FLAGS) /GS-" должно работать. другие опции работают? проверьте версию компилятора может что то изменили
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #6 : 22-09-2016 13:33 » |
|
Нет, не работает. 1>d:\Кирилл\Программы\драйвер\cl : warning D9025 : overriding '/GS' with '/GS-'
|
|
|
Записан
|
|
|
|
Ллирик
|
|
« Ответ #7 : 23-09-2016 15:38 » |
|
другие опции работают
|
|
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #8 : 23-09-2016 16:13 » |
|
хм. посмотрел мейк от DDK7600. не знаю как в вашей версии, но возможно параметр /GS определяется там принудительно, хотя сильно не вникал. а раз так - то может и не стоит его отключать.
тогда ищите причину ошибки. DeviceExtensionSize вроде не сказано что должно быть больше 0 но вдруг? выравнивание структур сколько стоит? не помню уже сколько там в x64 должно быть?
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #9 : 23-09-2016 19:17 » |
|
__security_init_cookie бсодит на строке .data:0000000000015100 __security_cookie dq 2B992DDFA232h , а IoCreateDevice на строке PDEVICE_OBJECT KeyDeviceObject;
|
|
« Последнее редактирование: 23-09-2016 19:21 от Ллирик »
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #10 : 23-09-2016 21:47 » |
|
может ей не нравится использование глобальной неинициализированной переменной?
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #11 : 23-09-2016 22:55 » |
|
Но ведь переменную __security_cookie dq 2B992DDFA232h создавал не я. И что мне со всем этим делать?
|
|
|
Записан
|
|
|
|
Aether
|
|
« Ответ #12 : 24-09-2016 09:22 » |
|
Можно для себя поинтересоваться: почему переменная объявлена в блоке .data, а в коде идёт использование cs? То есть, при программировании драйвера дескриптор кода и данных один и тот же? Или же это просто формат вывода дизассемблера?
|
|
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #13 : 26-09-2016 11:48 » |
|
Ллирик, рассказывайте какой версией компилятора/линкера пользуетесь. какие настройки проекта.
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #14 : 26-09-2016 13:24 » |
|
BUILD: corrupt database (Directory error) BUILD: Computing Include file dependencies: BUILD: Examining d:\oacr invalidate root:amd64fre /autocleanqueue 1>Compiling and Linking d:\1>'nmake.exe /nologo BUILDMSG=Stop. -i BUILD_PASS=PASS2 LINKONLY=1 NOPASS0=1 MAKEDIR_RELATIVE_TO_BASEDIR=' 1>d:\1> C:\WinDDK\7600.16385.1\Bin\amd64\oacr\oacrcl @d:\1>Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.207 for x64 1>Copyright (C) Microsoft Corporation. All rights reserved. 1>cl /Fo"d:\1> /FC 1> /Iamd64\ 1> /I. 1> /I..\inc 1> /Id:\1> /IC:\WinDDK\7600.16385.1\inc\api 1> /IC:\WinDDK\7600.16385.1\inc\api 1> /IC:\WinDDK\7600.16385.1\inc\ddk 1> /IC:\WinDDK\7600.16385.1\inc\ddk 1> /IC:\WinDDK\7600.16385.1\inc\crt 1> /D_WIN64 1> /D_AMD64_ 1> /DAMD64 1> /DCONDITION_HANDLING=1 1> /DNT_UP=1 1> /DNT_INST=0 1> /DWIN32=100 1> /D_NT1X_=100 1> /DWINNT=1 1> /D_WIN32_WINNT=0x0601 1> /DWINVER=0x0601 1> /D_WIN32_IE=0x0800 1> /DWIN32_LEAN_AND_MEAN=1 1> /DDEVL=1 1> /D__BUILDMACHINE__=WinDDK 1> /DNDEBUG 1> /D_DLL=1 1> /DNDEBUG 1> /DNTDDI_VERSION=0x06010000 1> /c 1> /Zc:wchar_t- 1> /Zl 1> /Zp8 1> /Gy 1> -cbstring 1> /W3 1> /WX 1> /EHs-c- 1> /GR- 1> /GF 1> /GS 1> /Zi 1> /Oxs 1> /GL 1> /Zi 1> /Fdd:\1> /GS- 1>cl : Command line warning D9025 : overriding '/GS' with '/GS-' 1>warnings in directory d:\1> /DKMDF_MAJOR_VERSION_STRING=01 1> /DKMDF_MINOR_VERSION_STRING=009 1> /typedil- 1> /wd4603 1> /wd4627 1> /FIC:\WinDDK\7600.16385.1\inc\api\warning.h 1> .\kmkm.c 1>kmkm.c 1> C:\WinDDK\7600.16385.1\Bin\amd64\oacr\oacrlink /out:d:\1>Microsoft (R) Incremental Linker Version 9.00.30729.207 1>Copyright (C) Microsoft Corporation. All rights reserved. 1>/MERGE:_PAGE=PAGE 1>/MERGE:_TEXT=.text 1>/SECTION:INIT,d 1>/OPT:REF 1>/OPT:ICF 1>/IGNORE:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221,4108,4088,4218,4218,4235 1>/INCREMENTAL:NO 1>/release 1>/NODEFAULTLIB 1>/WX 1>/debug 1>/debugtype:cv,fixup,pdata 1>/version:6.1 1>/osversion:6.1 1>C:\WinDDK\7600.16385.1\lib\win7\amd64\hotpatch.obj 1>/functionpadmin:6 1>/pdbcompress 1>/STACK:0x40000,0x1000 1>/driver 1>/base:0x10000 1>/subsystem:native,6.01 1>/entry:GsDriverEntry 1>/out:d:\1>d:\1>d:\1>C:\WinDDK\7600.16385.1\lib\win7\amd64\BufferOverflowK.lib 1>C:\WinDDK\7600.16385.1\lib\win7\amd64\ntoskrnl.lib 1>C:\WinDDK\7600.16385.1\lib\win7\amd64\hal.lib 1>C:\WinDDK\7600.16385.1\lib\win7\amd64\wmilib.lib 1>kmkm.obj : MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance 1>Microsoft (R) Incremental Linker Version 9.00.30729.207 1>Copyright (C) Microsoft Corporation. All rights reserved. 1>/MERGE:_PAGE=PAGE 1>/MERGE:_TEXT=.text 1>/SECTION:INIT,d 1>/OPT:REF 1>/OPT:ICF 1>/IGNORE:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221,4108,4088,4218,4218,4235 1>/INCREMENTAL:NO 1>/release 1>/NODEFAULTLIB 1>/WX 1>/debug 1>/debugtype:cv,fixup,pdata 1>/version:6.1 1>/osversion:6.1 1>C:\WinDDK\7600.16385.1\lib\win7\amd64\hotpatch.obj 1>/functionpadmin:6 1>/pdbcompress 1>/STACK:0x40000,0x1000 1>/driver 1>/base:0x10000 1>/subsystem:native,6.01 1>/entry:GsDriverEntry 1>/out:d:\1>d:\1>d:\1>C:\WinDDK\7600.16385.1\lib\win7\amd64\BufferOverflowK.lib 1>C:\WinDDK\7600.16385.1\lib\win7\amd64\ntoskrnl.lib 1>C:\WinDDK\7600.16385.1\lib\win7\amd64\hal.lib 1>C:\WinDDK\7600.16385.1\lib\win7\amd64\wmilib.lib 1>Generating code 1>Finished generating code
|
|
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #15 : 26-09-2016 14:18 » |
|
для начала обновите WDK до последней версии. WDK 7600.16385.1 - выпущена лет за пять до того как появилась Win10x64
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #16 : 26-09-2016 14:42 » |
|
для начала обновите WDK до последней версии. Так там для сборки требуется Visual Studio последней версии, а у меня её нет, поэтому и установил WDK 7600.16385.1
|
|
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #17 : 27-09-2016 10:25 » |
|
WDK 7600.16385.1 выпущена в 2010 году и предназначена для компиляции драйверов windows 7. гарантии что драйвера скомпилированные в ней подойдут для win 10 я не дам) как минимум в этом случае необходимо проверить как драйвер поведет себя на системе win 7 x64. да и то это не гарантия что можно продолжать пользоваться DDK 7 для win 10.
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #18 : 27-09-2016 21:58 » |
|
Установил Visual studio 2015 и WDK, но студия не видит инкулоидов WDK Добавлено через 3 часа, 46 минут и 41 секунду:Прописал путь к инкулоидам в ручную, собрал. На __security_cookie не бсодит, потому что нет строки INIT:000000000001732B lea rcx, __security_cookie . IoCreateDevice я пока отключил. Теперь бсодит вот на чём INIT:0000000140009126 lea rdx, gzlpKeyDataValueName ; "KeyData0" . Соответственно вопрос: как сделать так, чтобы компилятор всегда добавлял к переменным, находящимся в сегменте Pure data Read/Write, "cs:"? Вот так lea rdx, cs:gzlpKeyDataValueName
|
|
« Последнее редактирование: 28-09-2016 01:45 от Ллирик »
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #19 : 28-09-2016 08:56 » |
|
Теперь бсодит вот на чём INIT:0000000140009126 lea rdx, gzlpKeyDataValueName ; "KeyData0" это что вообще? приведите код на Cи мне кажется вы как-то использовали немного нестандартную инициализацию переменных...
|
|
« Последнее редактирование: 28-09-2016 08:59 от Ochkarik »
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #20 : 28-09-2016 13:09 » |
|
USHORT gzlpKeyDataValueName[] = {'K','e','y','D','a','t','a','0',0}; ...................................................................... gzlpKeyDataValueName[7] +=1; , Но я уверен, что если я опять включу PDEVICE_OBJECT KeyDeviceObject;
NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { UNICODE_STRING QgusDeviceName; NTSTATUS status;
RtlInitUnicodeString(&QgusDeviceName, L"\\Device\\KeyMouse"); status = IoCreateDevice(DriverObject, 0, &QgusDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &KeyDeviceObject); ........................................................................................ , снова будет бсодить на "&KeyDeviceObject"
|
|
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #21 : 28-09-2016 15:24 » |
|
WCHAR pszName[64]; // строка для формирования имени UNICODE_STRING uniName; // имя
//формируем имя объекта устройства с номером InstanceCount (0,1,2,...,99): "XXXX_NN" RtlStringCbPrintfW(pszName, 63 * sizeof(WCHAR), L"%s%02d", L"XXXX_", InstanceCount); RtlInitUnicodeString(&uniName, pszName);
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #22 : 28-09-2016 18:24 » |
|
А причём тут имя устройства? Имя устройства одно - L"\\Device\\KeyMouse". Бсодит на .data:0000000140005088 ; PDEVICE_OBJECT KeyDeviceObject .data:0000000140005088 KeyDeviceObject dq 0 ; DATA XREF: KbFilter_DispatchPassThroughr .data:0000000140005088 ; DriverEntry+3Do
. То есть при INIT:000000014000903D lea rax, KeyDeviceObject , а вот к примеру такая инструкция mov rax, cs:KeyDeviceObject не бсодит. А USHORT gzlpKeyDataValueName[] = {'K','e','y','D','a','t','a','0',0}; ...................................................................... gzlpKeyDataValueName[7] +=1; лишь пример бсода на lea INIT:0000000140009126 lea rdx, gzlpKeyDataValueName ; "KeyData0" и никакого отношения к IoCreateDevice он не имеет, но по-моему он всё же эффективнее вышеприведённого Вами кода
|
|
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #23 : 29-09-2016 08:07 » |
|
я привел пример как надо делать чтобы не было ошибок) все остальное от лукавого) операционная система вообще не эффективная штука, ассемблер куда эффективнее, особенно если не интересует время затраченное на разработку.
попробуйете в другом сегменте разместить. при помощи pragma
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #24 : 29-09-2016 13:41 » |
|
попробуйете в другом сегменте разместить. при помощи pragma
В каком например?
|
|
|
Записан
|
|
|
|
Ochkarik
|
|
« Ответ #25 : 29-09-2016 17:08 » |
|
|
|
|
Записан
|
RTFM уже хоть раз наконец! :[ ну или хотя бы STFW...
|
|
|
Ллирик
|
|
« Ответ #26 : 29-09-2016 17:27 » |
|
А кто тогда знает Добавлено через 1 день, 27 минут и 49 секунд:Решил попробовать так #pragma bss_seg("noinitdata") PDEVICE_OBJECT KeyDeviceObject; ................................................... #pragma bss_seg()
#pragma data_seg("initdata") USHORT gzlpKeyDataValueName[] = { 'K','e','y','D','a','t','a','0',0 }; UCHAR MouseButton = 1; #pragma data_seg()
#pragma comment(linker, "/SECTION:noinitdata, RWS") #pragma comment(linker, "/SECTION:initdata, RWS") , но error LNK1276: invalid directive 'RWS' found; does not start with '/' Почему? Добавлено через 1 день, 23 часа, 55 минут и 49 секунд:Слинкеровал вот с такими параметрами -section:.data,rws -section:.bss,rws . Не помогло Всё тот же BSOD Добавлено через 18 часов, 4 минуты и 6 секунд:Попробовал вот так #pragma text_seg(".text") PDEVICE_OBJECT KeyDeviceObject; #pragma text_seg() , не работает. Всё равно KeyDeviceObject помещается в сегмент ".data", а не в сегмент ".text"
|
|
« Последнее редактирование: 02-10-2016 17:51 от Ллирик »
|
Записан
|
|
|
|
Ллирик
|
|
« Ответ #27 : 12-10-2016 00:54 » |
|
Теперь я вообще ничего не понимаю Причём тут глобальные переменные когда бсодит case IRP_MN_QUERY_DEVICE_RELATIONS: if (devExt->LlirDeviceType == LLIR_DEVICE_KEYBOARD) { if (irpStack->Parameters.QueryDeviceRelations.Type == BusRelations) { status = IoCreateDevice(DeviceObject->DriverObject, sizeof (BUS_CHILD_DEVICE_EXTENSION), NULL, FILE_DEVICE_BUS_EXTENDER, FILE_AUTOGENERATED_DEVICE_NAME |FILE_DEVICE_SECURE_OPEN, FALSE, &devExt->BusMDeviceObject); if (status == STATUS_SUCCESS) { PDEVICE_OBJECT BusDeviceObject = devExt->BusMDeviceObject; BusDeviceObject->Flags |= DO_POWER_PAGABLE; BusDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; bdevExt = (PBUS_CHILD_DEVICE_EXTENSION) BusDeviceObject->DeviceExtension; bdevExt->LlirDeviceType = LLIR_DEVICE_BUS_CHILD; bdevExt->KeyboardDeviceObject = DeviceObject; bdevExt->outdata.UnitId = devExt->NumberDevice; oldrel = (PDEVICE_RELATIONS) Irp->IoStatus.Information; if (oldrel) { // extend existing list ULONG size = sizeof(DEVICE_RELATIONS) + (oldrel->Count) * sizeof(PDEVICE_OBJECT); newrel = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool, size); if (newrel) { // copy & extend RtlCopyMemory(newrel, oldrel, size - sizeof(PDEVICE_OBJECT)); ExFreePool(oldrel); } // copy & extend } // extend existing list else { // create new list newrel = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); newrel->Count = 0; } // create new list
if (newrel) { // build new list ObReferenceObject(BusDeviceObject); newrel->Objects[newrel->Count++] = BusDeviceObject; Irp->IoStatus.Information = (ULONG_PTR) newrel; } // build new list else status = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Status = status;
} } } else { if (irpStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) { newrel = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); if (!newrel) { status = STATUS_INSUFFICIENT_RESOURCES; } else { newrel->Count = 1; newrel->Objects[0] = DeviceObject; ObReferenceObject(DeviceObject);
status = STATUS_SUCCESS; Irp->IoStatus.Information = (ULONG_PTR) newrel; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } } break; В MSDN https://msdn.microsoft.com/en-us/library/windows/hardware/ff550671(v=vs.85).aspx написано ULONG_PTR Information; . Так почему мы пытаемся запихать ULONGULONG в ULONG? Попробовал так case IRP_MN_QUERY_DEVICE_RELATIONS: if (devExt->LlirDeviceType == LLIR_DEVICE_KEYBOARD) { if (irpStack->Parameters.QueryDeviceRelations.Type == BusRelations) { status = IoCreateDevice(DeviceObject->DriverObject, sizeof (BUS_CHILD_DEVICE_EXTENSION), NULL, FILE_DEVICE_BUS_EXTENDER, FILE_AUTOGENERATED_DEVICE_NAME |FILE_DEVICE_SECURE_OPEN, FALSE, &devExt->BusMDeviceObject); if (status == STATUS_SUCCESS) { PDEVICE_OBJECT BusDeviceObject = devExt->BusMDeviceObject; BusDeviceObject->Flags |= DO_POWER_PAGABLE; BusDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; bdevExt = (PBUS_CHILD_DEVICE_EXTENSION) BusDeviceObject->DeviceExtension; bdevExt->LlirDeviceType = LLIR_DEVICE_BUS_CHILD; bdevExt->KeyboardDeviceObject = DeviceObject; bdevExt->outdata.UnitId = devExt->NumberDevice; oldrel = (PDEVICE_RELATIONS) Irp->IoStatus.Information; if (oldrel) { // extend existing list ULONG size = sizeof(DEVICE_RELATIONS) + (oldrel->Count) * sizeof(PDEVICE_OBJECT); newrel = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool, size); if (newrel) { // copy & extend RtlCopyMemory(newrel, oldrel, size - sizeof(PDEVICE_OBJECT)); ExFreePool(oldrel); } // copy & extend } // extend existing list else { // create new list newrel = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); newrel->Count = 0; } // create new list
if (newrel) { // build new list ObReferenceObject(BusDeviceObject); newrel->Objects[newrel->Count++] = BusDeviceObject; (PDEVICE_RELATIONS) Irp->IoStatus.Information = newrel; } // build new list else status = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Status = status;
} } } else { if (irpStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) { newrel = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); if (!newrel) { status = STATUS_INSUFFICIENT_RESOURCES; } else { newrel->Count = 1; newrel->Objects[0] = DeviceObject; ObReferenceObject(DeviceObject);
status = STATUS_SUCCESS; (PDEVICE_RELATIONS) Irp->IoStatus.Information = newrel; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } } break; , Тот же бсод
|
|
|
Записан
|
|
|
|
zubr
Гость
|
|
« Ответ #28 : 12-10-2016 06:06 » |
|
ULONG_PTR в x64 будет иметь размерность 8 байт.
|
|
|
Записан
|
|
|
|
Ллирик
|
|
« Ответ #29 : 12-10-2016 12:45 » |
|
ULONG_PTR в x64 будет иметь размерность 8 байт.
Тогда почему бсодит?
|
|
|
Записан
|
|
|
|
|