前言

 

 

传说中的VT貌似很神秘的样子,关于VT入门的资料又很少,于是研究了一番

由于资源有限,自身水平亦有限,并且是闭门造车之作,如有错误的地方请指正,不胜感激!

关于VT可以先参考海风月影写的关于VT调试器http://bbs.pediy.com/showthread.PHP?t=96122

 

运行环境

操作系统: windows XP 

CPU : intel i3-390M 

状态: 单核运行

 

驱动没有卸载部分,测试前请先保存好文档.在 boot.ini 文件中添加 /numproc=1,重启

VT简介

 

 

Intel VIrtual Techonlogy , intel 硬件虚拟化技术 ,在硬件级别上完成计算机的虚拟化

 

为实现硬件虚拟化 ,VT增加了 12条新的 VMX指令

 

[VMCS控制 5 条]

VMPTRLD 

VMPTRST 

VMCLEAR 

VMREAD 

VMWRITE 

 

[VMX命令 5条]

VMLAUNCH 

VMCALL 

VMXON 

VMXOFF 

VMRESUME 

 

条]

INVEPT 

INVVPID 

 

 

条指令对应的机器码(xen-3.4.1\xen\include\asm-x86\hvm\vmx\vmx.h)

代码中使用 _emit

 

#define VMCALL_OPCODE ".byte 0x0f,0x01,0xc1\n"

#define VMCLEAR_OPCODE ".byte 0x66,0x0f,0xc7\n" /* reg/opcode: /6 */

#define VMLAUNCH_OPCODE ".byte 0x0f,0x01,0xc2\n"

#define VMPTRLD_OPCODE ".byte 0x0f,0xc7\n" /* reg/opcode: /6 */

#define VMPTRST_OPCODE ".byte 0x0f,0xc7\n" /* reg/opcode: /7 */

#define VMREAD_OPCODE ".byte 0x0f,0x78\n"

#define VMRESUME_OPCODE ".byte 0x0f,0x01,0xc3\n"

#define VMWRITE_OPCODE ".byte 0x0f,0x79\n"

#define INVEPT_OPCODE ".byte 0x66,0x0f,0x38,0x80\n" /* m128,r64/32 */

#define INVVPID_OPCODE ".byte 0x66,0x0f,0x38,0x81\n" /* m128,r64/32 */

#define VMXOFF_OPCODE ".byte 0x0f,0x01,0xc4\n"

#define VMXON_OPCODE ".byte 0xf3,0x0f,0xc7\n"

 

 

驱动初在始化一个VMCS (Virtual Machine Control Structures)内存区域后,启动VMM (Virtual Machine Monitor) , 

得到最高权限从而管理硬件资源, 操作系统是运行于ring0下

VMM要监控管理整个系统的资源,因而VMM的权限是大于操作系统,它处于一个全新的级别ring -1

用一个形象的比喻来讲就是:当前操作系统的的“政权”被VMM颠覆了

下面这个图是: VMM 与Guest OS 的联系

 



 

 

下面简单介绍一下VMM的建立过程,看看“政权”是如何被颠覆的

首先通过CPUID检查CPU是否支持VT

 

硬件环境的检测,通过指令CPUID检测CPU是否支持VT

个bit标志代表对 VT 的支持与否

(更多检测可以看

http://bbs.pediy.com/showthread.php?t=96122

回贴部分)

初始化主要的内存区域

使用VMXON 进入 VMX (虚拟机指令扩展指令集)操作

 

执行VMXON指令 , EFLAGS.CF 可判断执行是否成功

__asm

{

PUSH 0

PUSH PhysicalVMXONRegionPtr.LowPart

 

_emit 0xF3 // VMXON [ESP]

_emit 0x0F

_emit 0xC7

_emit 0x34

_emit 0x24

 

PUSHFD

POP eFlags

 

ADD ESP, 8

}

 

接下来开始初始化VMCS区域,(相当于部署军队,准备起义)

这个结构只能够被VMCLEAR, VMPTRLD, VMREAD,和VMWRITE操作。

VMM运行后guest 执行这些指令时会引发#VMExit事件

 

VMCS 结构如下图

 

 

 

 

VMCS 是一个4K的内存区域

在逻辑上,虚拟机控制结构被划分为 6 部分:

1) GUEST-STATE 域:虚拟机从根操作模式进入非根操作模式时,处理器所处的状态;

2) HOST-STATE 域:虚拟机从非根操作模式退出到根操作模式时,处理器所处的状态;

3) VM 执行控制域:虚拟机在非根操作模式运行的时候,控制处理器非根操作模式退出到根操作模式;

4) VM 退出控制域:虚拟机从非根操作模式下退出时,需要保存的信息;

5) VM 进入控制域:虚拟机从根操作模式进入非根操作模式时,需要读取的信息;

6) VM 退出信息域:虚拟机从非根操作模式退出到根操作模式时,将退出的原因保存到该域中。

 

 

在这里初始化的代码比较多,要初始化 host 与 guest 的 CR0,CR3,CR4,IDTR,GDTR,LDTR,Rflag,SYSENTER_CS,SYSENTER_EIP,SYSENTER_ESP等

初始化时使用VMWRITE 指令设置 , 设置时主要的宏是

GUEST_ES_SELECTOR = 0x00000800,

GUEST_CS_SELECTOR = 0x00000802,

GUEST_SS_SELECTOR = 0x00000804,

GUEST_DS_SELECTOR = 0x00000806,

GUEST_FS_SELECTOR = 0x00000808,

GUEST_GS_SELECTOR = 0x0000080a,

GUEST_LDTR_SELECTOR = 0x0000080c,

GUEST_TR_SELECTOR = 0x0000080e,

HOST_ES_SELECTOR = 0x00000c00,

HOST_CS_SELECTOR = 0x00000c02,

HOST_SS_SELECTOR = 0x00000c04,

HOST_DS_SELECTOR = 0x00000c06,

HOST_FS_SELECTOR = 0x00000c08,

HOST_GS_SELECTOR = 0x00000c0a,

HOST_TR_SELECTOR = 0x00000c0c,



使用 VMWRITE 指令集成的函数

VOID WriteVMCS( ULONG encoding, ULONG value )

{

__asm

{

PUSHAD

PUSH value

MOV EAX, encoding 

 

_emit 0x0F 

_emit 0x79 

_emit 0x04

_emit 0x24

 

POP EAX

POPAD

}

}

例如初始化 GDT , IDT 使用的是以下代码

__asm

{

SGDT gdt_reg

}

temp32 = 0;

temp32 = gdt_reg.BaseHi;

temp32 <<= 16;

temp32 |= gdt_reg.BaseLo;

Log( "Setting Host GDTR Base" , temp32 );

WriteVMCS( HOST_GDTR_BASE, temp32 ); 

 

 

__asm

{

SIDT idt_reg

}

temp32 = 0;

temp32 = idt_reg.BaseHi;

temp32 <<= 16;

temp32 |= idt_reg.BaseLo;

 

Log( "Setting Host IDTR Base" , temp32 );

WriteVMCS( HOST_IDTR_BASE, temp32 );

 

比较重要的是设置 #VMExit 事件处理入口 HOST_RIP

WriteVMCS( HOST_RIP, (ULONG)VMMEntryPoint ); //0x6C16

 

最后执行 VMLAUNCH 指令 ,正式启动虚拟机(建立了新的政权)

__asm

{

_emit 0x0F 

_emit 0x01

_emit 0xC2

}

至此,整个VMM的帝国已经运行起来,帝国将会管理整个系统资源

接下来的就是#VMExit的事件循环处理(类似在调试器中下断点后,等待事件发生)

运行过程中Guest OS 遇到要监控的指令,会发生 #VMExit 事件 (相当于被调试程序执行到中断事件)

此时由VMM 处理,例如cpuid , 

处理完后由 VMRESUME 继续执行 (相当于在 OD 中 F9 继续运行)

 

#VMExit事件的类型

 

#VMExit事件分为二种

1 无条件事件CPUID GETSEC INVD XSETBV 所有 VMX指令

2 有条件事件I/O访问,中断事件, MSR寄存器访问, HTL 等 (要设置VMCS相应部分触发)

 

 

 

VMM使用VMREAD读取虚拟机状态,主要的一些变量有

VM_EXIT_REASON = 0x00004402, //退出代码

VM_EXIT_INTR_INFO = 0x00004404, //中断信息

VM_EXIT_INTR_ERROR_CODE = 0x00004406,

IDT_VECTORING_INFO = 0x00004408,

IDT_VECTORING_ERROR_CODE = 0x0000440a,

VM_EXIT_INSTRUCTION_LEN = 0x0000440c, //指令长度

VMX_INSTRUCTION_INFO = 0x0000440e,

GUEST_ES_LIMIT = 0x00004800,

GUEST_CS_LIMIT = 0x00004802,

GUEST_SS_LIMIT = 0x00004804,

GUEST_DS_LIMIT = 0x00004806,



其中最重要的就是VM_EXIT_REASON , 可以看作是消息类型

主要的类型有

#define EXIT_REASON_EXCEPTION_NMI 0

#define EXIT_REASON_EXTERNAL_INTERRUPT 1

#define EXIT_REASON_TRIPLE_FAULT 2

#define EXIT_REASON_INIT 3

#define EXIT_REASON_SIPI 4

#define EXIT_REASON_IO_SMI 5

#define EXIT_REASON_OTHER_SMI 6

#define EXIT_REASON_PENDING_VIRT_INTR 7

#define EXIT_REASON_PENDING_VIRT_NMI 8

#define EXIT_REASON_TASK_SWITCH 9

#define EXIT_REASON_CPUID 10

#define EXIT_REASON_HLT 12

#define EXIT_REASON_INVD 13

#define EXIT_REASON_INVLPG 14

#define EXIT_REASON_RDPMC 15

#define EXIT_REASON_RDTSC 16

#define EXIT_REASON_RSM 17

#define EXIT_REASON_VMCALL 18

#define EXIT_REASON_VMCLEAR 19

#define EXIT_REASON_VMLAUNCH 20

#define EXIT_REASON_VMPTRLD 21

#define EXIT_REASON_VMPTRST 22

#define EXIT_REASON_VMREAD 23

#define EXIT_REASON_VMRESUME 24

#define EXIT_REASON_VMWRITE 25

#define EXIT_REASON_VMXOFF 26

#define EXIT_REASON_VMXON 27



 

DWORD VmxRead(DWORD in_code)

{

DWORD m_vmread = 0;

__asm

{

PUSHAD

 

MOV EAX, in_code 

_emit 0x0F // VMREAD EBX, EAX

_emit 0x78

_emit 0xC3

 

MOV m_vmread, EBX 

POPAD

}

return m_vmread; 

}

 

VOID VMMReadGuestState( )

{

HandlerLogging = 0;

ExitReason = VmxRead(VM_EXIT_REASON); //0x4402

ExitInterruptionInformation = VmxRead( VM_EXIT_INTR_INFO); //0x4404

ExitInstructionLength = VmxRead(VM_EXIT_INSTRUCTION_LEN); //0x440c

ExitQualification = VmxRead(EXIT_QUALIFICATION) ; //0x6400

ExitInterruptionInformation = VmxRead(VM_EXIT_INTR_INFO); //0x4404

ExitInterruptionErrorCode = VmxRead(VM_EXIT_INTR_ERROR_CODE); //0x4406

 

IDTVectoringInformationField = VmxRead(IDT_VECTORING_INFO); //0X00004408 // IDT-Vectoring Information Field

IDTVectoringErrorCode = VmxRead(IDT_VECTORING_ERROR_CODE); //0X0000440A // IDT-Vectoring Error Code 

ExitInstructionLength = VmxRead(VM_EXIT_INSTRUCTION_LEN); //0x0000440C // VM-Exit Instruction Length

ExitInstructionInformation = VmxRead(VMX_INSTRUCTION_INFO) ; //0x0000440E //VM-Exit Instruction Information

GuestEIP = VmxRead(GUEST_RIP); //0x0000681E; //GuestEIP

GuestESP = VmxRead(GUEST_RSP); //0x0000681c //esp

GuestCR3 = VmxRead(GUEST_CR3); //0X6802 GuestCR3

 

}

 

CPUID指令的拦截与修改

 

 

当 guest OS 执行 cupid 时,会得到这样一个错误号 10 (VMX_EXIT_CPUID )

当执行到CPUID这条指令的时候,响应,然后就可以修改cpuid的返回值

 

if( ExitReason == VMX_EXIT_CPUID ) 



if( GuestEAX == 0 )

{

DbgPrint("CPUID EIP == %08X \n" , GuestEIP );

//0x34EC2B 

__asm

{

POPAD

MOV EAX, 0

CPUID 

 

 

//修改CPUID返回值

MOV EBX, 0x80808080

MOV ECX, 0x90909090

MOV EDX, 0x10101010

JMP Resume

}

}

else

{

__asm

{

POPAD

MOV EAX, GuestEAX

CPUID

JMP Resume





}

 

 

最后运行的效果

 

运行VT前 cupid



 

运行VT后 cpuid

 



 

顺道打印了执行CPUID程序的 EIP,与输出的 00401009一致

00401003 |. 53 push ebx

00401004 |? B8 00000000 mov eax,0

00401009 |. 0FA2 cpuid

0040100B |. 894424 0C mov dword ptr ss:[esp+C],eax

0040100F |? 894C24 08 mov dword ptr ss:[esp+8],ecx

00401013 |? 895C24 04 mov dword ptr ss:[esp+4],ebx

00401017 |? 68 10614000 push cpuid.00406110 ; ASCII "---------cpuid------",LF

EXIT_REASON 的所有类型注释



VirtualBox\src\VBox\VMM\VMMR3\HWACCM.cpp



EXIT_REASON(VMX_EXIT_EXCEPTION          ,  0, "Exception or non-maskable interrupt (NMI)."),

    EXIT_REASON(VMX_EXIT_EXTERNAL_IRQ       ,  1, "External interrupt."),

    EXIT_REASON(VMX_EXIT_TRIPLE_FAULT       ,  2, "Triple fault."),

    EXIT_REASON(VMX_EXIT_INIT_SIGNAL        ,  3, "INIT signal."),

    EXIT_REASON(VMX_EXIT_SIPI               ,  4, "Start-up IPI (SIPI)."),

    EXIT_REASON(VMX_EXIT_IO_SMI_IRQ         ,  5, "I/O system-management interrupt (SMI)."),

    EXIT_REASON(VMX_EXIT_SMI_IRQ            ,  6, "Other SMI."),

    EXIT_REASON(VMX_EXIT_IRQ_WINDOW         ,  7, "Interrupt window."),

    EXIT_REASON_NIL(),

    EXIT_REASON(VMX_EXIT_TASK_SWITCH        ,  9, "Task switch."),

    EXIT_REASON(VMX_EXIT_CPUID              , 10, "Guest software attempted to execute CPUID."),

    EXIT_REASON_NIL(),

    EXIT_REASON(VMX_EXIT_HLT                , 12, "Guest software attempted to execute HLT."),

    EXIT_REASON(VMX_EXIT_INVD               , 13, "Guest software attempted to execute INVD."),

    EXIT_REASON(VMX_EXIT_INVPG              , 14, "Guest software attempted to execute INVPG."),

    EXIT_REASON(VMX_EXIT_RDPMC              , 15, "Guest software attempted to execute RDPMC."),

    EXIT_REASON(VMX_EXIT_RDTSC              , 16, "Guest software attempted to execute RDTSC."),

    EXIT_REASON(VMX_EXIT_RSM                , 17, "Guest software attempted to execute RSM in SMM."),

    EXIT_REASON(VMX_EXIT_VMCALL             , 18, "Guest software executed VMCALL."),

    EXIT_REASON(VMX_EXIT_VMCLEAR            , 19, "Guest software executed VMCLEAR."),

    EXIT_REASON(VMX_EXIT_VMLAUNCH           , 20, "Guest software executed VMLAUNCH."),

    EXIT_REASON(VMX_EXIT_VMPTRLD            , 21, "Guest software executed VMPTRLD."),

    EXIT_REASON(VMX_EXIT_VMPTRST            , 22, "Guest software executed VMPTRST."),

    EXIT_REASON(VMX_EXIT_VMREAD             , 23, "Guest software executed VMREAD."),

    EXIT_REASON(VMX_EXIT_VMRESUME           , 24, "Guest software executed VMRESUME."),

    EXIT_REASON(VMX_EXIT_VMWRITE            , 25, "Guest software executed VMWRITE."),

    EXIT_REASON(VMX_EXIT_VMXOFF             , 26, "Guest software executed VMXOFF."),

    EXIT_REASON(VMX_EXIT_VMXON              , 27, "Guest software executed VMXON."),

    EXIT_REASON(VMX_EXIT_CRX_MOVE           , 28, "Control-register accesses."),

    EXIT_REASON(VMX_EXIT_DRX_MOVE           , 29, "Debug-register accesses."),

    EXIT_REASON(VMX_EXIT_PORT_IO            , 30, "I/O instruction."),

    EXIT_REASON(VMX_EXIT_RDMSR              , 31, "RDMSR. Guest software attempted to execute RDMSR."),

    EXIT_REASON(VMX_EXIT_WRMSR              , 32, "WRMSR. Guest software attempted to execute WRMSR."),

    EXIT_REASON(VMX_EXIT_ERR_INVALID_GUEST_STATE,  33, "VM-entry failure due to invalid guest state."),

    EXIT_REASON(VMX_EXIT_ERR_MSR_LOAD       , 34, "VM-entry failure due to MSR loading."),

    EXIT_REASON_NIL(),

    EXIT_REASON(VMX_EXIT_MWAIT              , 36, "Guest software executed MWAIT."),

    EXIT_REASON_NIL(),

    EXIT_REASON_NIL(),

    EXIT_REASON(VMX_EXIT_MONITOR            , 39, "Guest software attempted to execute MONITOR."),

    EXIT_REASON(VMX_EXIT_PAUSE              , 40, "Guest software attempted to execute PAUSE."),

    EXIT_REASON(VMX_EXIT_ERR_MACHINE_CHECK  , 41, "VM-entry failure due to machine-check."),

    EXIT_REASON_NIL(),

    EXIT_REASON(VMX_EXIT_TPR                , 43, "TPR below threshold. Guest software executed MOV to CR8."),

    EXIT_REASON(VMX_EXIT_APIC_ACCESS        , 44, "APIC access. Guest software attempted to access memory at a physical address on the APIC-access page."),

    EXIT_REASON_NIL(),

    EXIT_REASON(VMX_EXIT_XDTR_ACCESS        , 46, "Access to GDTR or IDTR. Guest software attempted to execute LGDT, LIDT, SGDT, or SIDT."),

    EXIT_REASON(VMX_EXIT_TR_ACCESS          , 47, "Access to LDTR or TR. Guest software attempted to execute LLDT, LTR, SLDT, or STR."),

    EXIT_REASON(VMX_EXIT_EPT_VIOLATION      , 48, "EPT violation. An attempt to access memory with a guest-physical address was disallowed by the configuration of the EPT paging structures."),

    EXIT_REASON(VMX_EXIT_EPT_MISCONFIG      , 49, "EPT misconfiguration. An attempt to access memory with a guest-physical address encountered a misconfigured EPT paging-structure entry."),

    EXIT_REASON(VMX_EXIT_INVEPT             , 50, "INVEPT. Guest software attempted to execute INVEPT."),

    EXIT_REASON_NIL(),

    EXIT_REASON(VMX_EXIT_PREEMPTION_TIMER   , 52, "VMX-preemption timer expired. The preemption timer counted down to zero."),

    EXIT_REASON(VMX_EXIT_INVVPID            , 53, "INVVPID. Guest software attempted to execute INVVPID."),

    EXIT_REASON(VMX_EXIT_WBINVD             , 54, "WBINVD. Guest software attempted to execute WBINVD."),

    EXIT_REASON(VMX_EXIT_XSETBV             , 55, "XSETBV. Guest software attempted to execute XSETBV."),

    EXIT_REASON_NIL()
  jpg改rar

Intel VT入门的更多相关文章

  1. VT 入门篇——最小 VT 实现(上)

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  2. VT 入门番外篇——初识 VT

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  3. 羽夏看Win系统内核—— VT 入门番外篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  4. intel vt

    EPT和VPID技术是内存虚拟化技术, 是页表扩充技术Extended Page Table (EPT) 的缩写, 是VT-x技术的一部分. 内存虚拟化的主要任务是实现地址空间的虚拟化,内存虚拟化是通 ...

  5. 虚拟机出现intel vt -x 处于禁用状态打不开处理方式

    处理方式 . 1 进入bios 以华硕主板为例 进入高级模式找到cpu虚拟技术 打开虚拟技术支持 其它电脑找到这个

  6. Intel VT-x 基本概念

    看IaaS 资料时,捎带研究下硬件虚拟化,主要参考<基于intel VT-x 的Xen 全虚拟化实现>,<intel 开发手册 第三卷 19/20章> Intel VT 是in ...

  7. 用x86的模拟器内核记得安装intel的haxm

    Android 模拟器一直以运行速度慢著称, 本文介绍使用 Intel HAXM 技术为 Android 模拟器加速, 使模拟器运行度媲美真机, 彻底解决模拟器运行慢的问题. Intel HAXM ( ...

  8. 虚拟化之intel

    英特尔VT具体包括分别针对处理器.芯片组.网络的VT-X.VT-D和VT-C技术. 处理器:英特尔虚拟化技术(英特尔VT-x),包括英特尔虚拟化灵活迁移技术(Intel VT FlexMigratio ...

  9. 【转】安装Intel HAXM为Android 模拟器加速,30秒内启动完成

    http://www.cnblogs.com/Li-Cheng/p/4351966.html http://www.cnblogs.com/csulennon/p/4178404.html https ...

随机推荐

  1. 利用HTML5的History API实现无刷新跳转页面初探

    HTML4中的History API history这个东西大家应该都不陌生,我们经常使用history.back(-1)来实现后退功能,具体的属性和方法如下: 属性 length 历史的项数.Jav ...

  2. Zabbix监控disk performance

    概述 zabbix获取/sys里面的磁盘信息并分析来监控disk performance sysfs是Linux内核中设计较新的一种虚拟的基于内存的文件系统,它的作用与 proc 有些类似(默认挂载在 ...

  3. MySQL字符串连接函数

    一.CONCAT(str1,str2,…) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. select concat(s_id, "--", ...

  4. <!DOCTYPE html>作用

    1.定义: DOCTYPE标签是一种标准通用标记语言的文档类型声明,它的目的是要告诉标准通用标记语言解析器,它应该使用什么样的文档类型定义(DTD)来解析文档. <!DOCTYPE> 声明 ...

  5. 介绍编译的less的两种IDE工具

    介绍编译的less的两种IDE工具 现在css预编译越来越普及了,著名的有less.sass.stylus等等等等.功能上基本上都是大同小异.这些个玩意儿主要表达的意思就是:"像编程一样的编 ...

  6. 【转】HTML5的小知识点小集合

    html5的小知识点小集合 html5知识   1.  Doctype作用?标准模式与兼容模式各有什么区别? (1).<!DOCTYPE>声明位于位于HTML文档中的第一行,处于<h ...

  7. linux下的apache配置文件详解

    .Apache的配置由httpd.conf文件配置,因此下面的配置指令都是在httpd.conf文件中修改. 站点的配置(基本配置) (1) 基本配置: ServerRoot "/mnt/s ...

  8. 巧用setTimeout解决阻塞导致页面无法重绘

    项目中遇到一个javascript问题,大致如下: <!doctype HTML> <html> <head> <meta charset="utf ...

  9. 5Hibernate配置及使用方法----青软S2SH(笔记)

    关于hibernate的简单配置,先看结构图,我们需要 1.还要弄一下需要的 jar包. 2.配置两个文件(hibernate配置文件和映射文件),不过映射文件可以用注解替代. 3.写一个pojo类, ...

  10. esnext:最后一个参数后面也允许加逗号了

    https://jeffmo.github.io/es-trailing-function-commas 目前是一个 stage 3 的提案,Chakra 和 JSC 已经实现了,它允许我们在函数定义 ...