精华内容
下载资源
问答
  • 最详实、权威的2014年中国互联网发展状况统计报告
  • 由中国互联网络信息中心 (CNNIC)权威发布,1998年起中国互联网络信息中心每年1月和7月发布中国互联网络发展状况统计报告。目前,最新版本是2009年1月发布第23次中国互联网络发展状况统计报告。详情查看:新浪网--...

          由中国互联网络信息中心 (CNNIC)权威发布,1998年起中国互联网络信息中心每年1月7月发布中国互联网络发展状况统计报告。目前,最新版本是2009年1月发布的第23次中国互联网络发展状况统计报告。详情查看:新浪网--第23次中国互联网络发展状况统计报告--专题 http://tech.sina.com.cn/focus/cnnic23/ 和中国互联网络信息中心 (CNNIC)http://www.cnnic.com.cn/ 报告下载:http://down1.tech.sina.com.cn/download/down_contents/1234022400/42562.shtml 
        本次报告关键词:
        1、我国网民数达2.98亿 手机上网用户首超1亿
        2、网上教育

      2008年网上教育的使用率为16.5%,基本与2007年持平。网上教育主要应用人群是中小学生和普通在职人员。

    第23次互联网发展报告

    表 16 2007-2008年网上教育用户对比

     

     

      校校通工程促进了中国的中小学学校互通与上网平台的建设,且近年来中小学生的课堂教育已不能满足家长们对孩子的期望,各种网上的补习班和课程都开始成为中小学生的学习内容。而随着就业压力的增大,已工作的普通在职人员更加注重专业能力的培养,英语、会计等网上教育课程,由于更容易分配时间,成本相对低廉,得到了在职人员的推崇。未来几年网上教育将会有较好的发展空间。


       

    转载于:https://www.cnblogs.com/liuzhengliang/archive/2009/03/03/1402275.html

    展开全文
  • 中国互联网络信息中心(CNNIC)于2009.1.13发布《第23次中国互联网络发展状况统计报告》,可谓中国最权威、最新互联网络发展统计,与同行共享。
  • 在此项报告中,FIT2CLOUD飞致云是唯一被独立统计的中立型多云管理厂商。这也是FIT2CLOUD飞致云第二次进入该项权威研究报告。相比IDC发布2018年下半年市场研究数据,FIT2CLOUD飞致云市场排名升至第五位,在中国云...

    近日,全球著名的ICT市场研究机构IDC(国际数据公司)发布了《2019年上半年中国云系统和服务管理软件市场追踪报告》。在此项报告中,FIT2CLOUD飞致云是唯一被独立统计的中立型多云管理厂商。这也是FIT2CLOUD飞致云第二次进入该项权威研究报告。相比IDC发布的2018年下半年市场研究数据,FIT2CLOUD飞致云的市场排名升至第五位,在中国云系统和服务管理软件市场中营收占比为5.1%

    “云系统和服务管理软件市场”跟踪统计厂商销售云管理软件获得的收入,其中包括软件许可、基础维保、软件订阅以及SaaS等形式的销售收入。云系统和服务管理软件可以被应用于管理私有云、公有云、混合云或多云环境。同时,该软件的纳管对象也包含虚拟机、容器平台等IT资源。

    企业云采纳率的提升,以及多云、混合云架构的应用在过去几年间持续拉动企业对云管理软件的需求。借助云管平台,企业实现了对现有IaaS资源的统一管理。而企业对容器的采纳和混合云架构的流行将在未来推动云管理软件市场新一轮的增长。

    IDC预测,到2020年,75%的公有云用户会同时使用企业级私有云平台。2024年,将有90%的全球1000强组织采用多云管理策略,其中包含对跨公有云和私有云环境集成工具的使用。

    自2019年5月起,FIT2CLOUD飞致云的市场表现数据连续进入IDC《中国云系统和服务管理软件市场追踪报告》,成为该项研究报告中唯一被独立统计的中立型多云管理厂商

    作为中国领先的多云管理软件及服务提供商,FIT2CLOUD飞致云致力于解决企业在多云环境下的服务化和管理、安全及数据库服务交付难题。目前,FIT2CLOUD云管平台在中国市场拥有广泛的安装基础,客户覆盖金融、制造、能源、交通运输、电信、传媒、地产、互联网等行业。2019年上半年,FIT2CLOUD云管平台新增用户主要来自银行、证券、制造、政府、交通运输、房地产等行业。

    伴随着云计算的企业实践从多云共存走向多云融合,FIT2CLOUD飞致云在助力企业IT云化和服务化转型的道路上不断创新前行。现阶段,推动企业构建统一多云管理平台的关键因素包括,企业对公有云和容器云的广泛采纳、VMware虚拟化资源池的云化升级需求,以及私有云(尤其是超融合系统)在企业环境的部署与运营。

    面对企业高效管理与运营混合IT基础设施的实际需求,FIT2CLOUD云管平台以统一的方式帮助企业用户在多云环境下实现自动化、自助式的服务交付(例如虚机服务、容器服务、物理机服务、对象存储服务等),同时持续提升企业的运营效率及安全合规管控水平,支持企业从传统IT渐进、无缝地过渡到多云战略。

    针对企业用户在IaaS层之上的服务化交付需求,FIT2CLOUD飞致云从2018年开始加大在容器领域的技术投入。目前,FIT2CLOUD云管平台已经实现了对容器云的统一管理,用户可通过容器云适配器管理多个Kubernetes集群,并且通过统一门户获得租户管理、权限控制、运营分析、配额申请、计量计费等多集群统一管理能力。

    此外,为了帮助企业用户更好地开展云原生实践,FIT2CLOUD飞致云还创立了容器开源项目——KubeOperator,并且推出FIT2CLOUD DBaaS(Database as a Service,数据库即服务)平台。

    其中,KubeOperator是一款容器集群管理平台,它在离线网络环境下,通过可视化Web UI在VMware、OpenStack或者物理机上规划、部署和运营生产级别的Kubernetes集群;FIT2CLOUD DBaaS平台则基于容器技术在多云环境下交付全托管(Fully-Managed)的MySQL、Redis等数据库服务,助力企业快速开展业务创新。

    “云计算已经全面进入应用时代,多云架构正在被企业广泛采纳,这一关键性趋势推动云管理软件走向成熟,市场规模高速增长。FIT2CLOUD飞致云长期深耕多云管理市场,通过持续的产品演进和不断优化的行业交付体验向企业用户交付云管理价值。


    目前,容器、超融合等创新技术的广泛应用给云管理软件带来了新的挑战和市场机遇,多云管理技术正在孕育新一轮的变革。FIT2CLOUD飞致云正在积极拥抱云原生等创新趋势,以云管平台为核心,有效推进企业混合IT基础设施充分云化和服务化的进程,不断拓展企业IT服务化的种类,助力企业实现数字化转型的目标。”


    —— FIT2CLOUD飞致云 CEO 阮志敏

    展开全文
  • 第一章 前言 ...近日,360互联网安全中心发现...该款勒索软件在短时间内在全球范围内爆发了广泛攻击活动,据不完全统计,它在爆发后几个小时内就迅速攻击了99个国家近万台设备,并在大量企业组织和个人间蔓


    360追日团队官网:http://zhuiri.360.cn/ 


    第一章 前言


    近日,360互联网安全中心发现全球多个国家和地区的机构及个人电脑遭受到了一款新型勒索软件攻击,并于5月12日国内率先发布紧急预警。该款勒索软件在短时间内在全球范围内爆发了广泛的攻击活动,据不完全统计,它在爆发后的几个小时内就迅速攻击了99个国家的近万台设备,并在大量企业组织和个人间蔓延。外媒和多家安全公司将其命名为“WanaCrypt0r”(直译:“想哭勒索蠕虫”)。


    通常,常规的勒索病毒是一种趋利明显的恶意程序,它会使用非对称加密算法加密受害者电脑内的重要文件并以此来进行勒索,向受害者索要赎金,除非受害者交出勒索赎金,否则被加密的文件无法被恢复。而新型勒索软件的“想哭勒索蠕虫”尤其致命,它利用了窃取自美国国家安全局的黑客工具EternalBlue(直译:“永恒之蓝”)实现了全球范围内的快速传播,在短时间内造成了巨大损失。继5月12日WanaCrypt0r全球攻击爆发以来,360核心安全部门对该勒索蠕虫保持了高度关注,部门各团队紧密协作,首家发布了一系列针对该蠕虫的查杀、免疫和文件恢复解决方案。


    360核心安全部门追日团队深入分析病毒原理,发现了其加密数据最精准的恢复技术,使用此技术360在全球独家发布了“想哭勒索蠕虫数据恢复工具”帮助病毒受害者恢复被蠕虫加密文件,可以达到目前最全最快的数据恢复效果,我们希望本篇技术报告可以帮助大家了解该蠕虫的核心技术原理,并对恢复被加密数据的可行性做进一步探讨。


    第二章 加密文件核心流程分析


    蠕虫释放一个加密模块到内存,直接在内存加载该DLL。DLL导出一个函数TaskStart用于启动整个加密的流程。


    程序动态获取文件系统和加密相关的API函数,以此来躲避静态查杀。



    1. 加密入口

    调用SHGetFolderPathW获取了桌面和文档文件夹的路径,调用10004A40函数获得非当前用户的桌面和文档文件夹,分别调用EncryptFolder对文件夹进行加密操作



    从Z倒序遍历盘符直到C,遍历两次,第一次遍历本地盘符(跳过光驱),第二次遍历移动盘符,分别调用EncryptFolder对文件夹进行加密操作


    2. 文件遍历

    EncryptFolder函数是一个递归函数,递归遍历文件夹,按照下图的描述搜集文件信息:



    遍历过程中排除的路径或者文件夹名称:

    去除路径中盘符或主机名后进行比较


    \\Intel

    \\ProgramData

    \\WINDOWS

    \\Program Files

    \\Program Files (x86)

    \\AppData\\Local\\Temp

    \\Local Settings\\Temp

    文件夹名称(完全相等)

    Temporary Internet Files

    This folder protects against ransomware. Modifying it will reduce protection

    Content.IE5


    其中有一个很有意思的目录名“ This folder protects against ransomware. Modifying it will reduce protection”,通过Google查询,发现其是国外的一款名为ransomfree的勒索防御软件创建的防御目录。


    在遍历文件的过程中,会获取文件信息(大小等),并且根据后缀名使用下表的规则对文件进行分类(type):


    type列表1:


    1
    ".doc",".docx",".xls",".xlsx",".ppt",".pptx",".pst",".ost",".msg",".eml",".vsd",".vsdx",".txt",".csv",".rtf",".123",".wks",".wk1",".pdf",".dwg",".onetoc2",".snt",".jpeg",".jpg"


    type列表2:



    1
    ".docb",".docm",".dot",".dotm",".dotx",".xlsm",".xlsb",".xlw",".xlt",".xlm",".xlc",".xltx",".xltm",".pptm",".pot",".pps",".ppsm",".ppsx",".ppam",".potx",".potm",".edb",".hwp",".602",".sxi",".sti",".sldx",".sldm",".sldm",".vdi",".vmdk",".vmx",".gpg",".aes",".ARC",".PAQ",".bz2",".tbk",".bak",".tar",".tgz",".gz",".7z",".rar",".zip",".backup",".iso",".vcd",".bmp",".png",".gif",".raw",".cgm",".tif",".tiff",".nef",".psd",".ai",".svg",".djvu",".m4u",".m3u",".mid",".wma",".flv",".3g2",".mkv",".3gp",".mp4",".mov",".avi",".asf",".mpeg",".vob",".mpg",".wmv",".fla",".swf",".wav",".mp3",".sh",".class",".jar",".java",".rb",".asp",".php",".jsp",".brd",".sch",".dch",".dip",".pl",".vb",".vbs",".ps1",".bat",".cmd",".js",".asm",".h",".pas",".cpp",".c",".cs",".suo",".sln",".ldf",".mdf",".ibd",".myi",".myd",".frm",".odb",".dbf",".db",".mdb",".accdb",".sql",".sqlitedb",".sqlite3",".asc",".lay6",".lay",".mml",".sxm",".otg",".odg",".uop",".std",".sxd",".otp",".odp",".wb2",".slk",".dif",".stc",".sxc",".ots",".ods",".3dm",".max",".3ds",".uot",".stw",".sxw",".ott",".odt",".pem",".p12",".csr",".crt",".key",".pfx",".der"


    3. 文件处理队列

    WanaCrypt0r为了能尽快的加密其认为重要的用户文件,设计了一套复杂的优先级队列。


    队列优先级:

    i. 对type2(满足后缀列表1)进行加密(小于0x400的文件会降低优先级)

    ii. 对type3(满足后缀列表2)进行加密(小于0x400的文件会降低优先级)

    iii. 处理剩下的文件(小于0x400的文件),或者其他一些文件


    4. 加密逻辑

    加密过程采用RSA+AES的方式完成,其中RSA加密过程使用了微软的CryptoAPI,AES代码则静态编译到DLL中。加密流程如下图所示:



    使用的密钥概述:



    加密后的文件格式示意:



    值得注意的是,在加密过程中,程序会随机选取一部分文件使用内置的RSA公钥来进行加密,这里的目的是解密程序提供的免费解密部分文件功能。



    能免费解密的文件路径在文件f.wnry中,如下:



    5. 随机数填充

    在完成加密之后,WanaCrypt0r会对其认为重要的文件进行随机数填充,然后将文件移动到指定的临时文件夹目录然后删除。此举用于对抗文件恢复类软件,同时兼顾加密文件的速度。


    被随机数填充的文件需要满足以下几点:

    在特殊目录(桌面,我的文档,用户文件夹)

    文件小于200M

    文件后缀在type列表1


    填充的逻辑:

    如果文件小于0x400,直接覆盖对应长度的随机数。

    如果文件大于0x400,对文件距离末尾0x400处进行覆盖。

    再次重定位文件指针到文件头,以0x40000大小的缓冲区为单位向写随机数直到文件末尾。




    6. 文件删除操作

    WanaCrypt0r首先尝试将样本移动到临时文件夹,生成一个临时文件,然后再尝试多种方法删除文件。


    当采用遍历磁盘的方式加密文件的时候,会在当前盘符生成“$RECYCLE”+ 全局自增量+”.WNCYRT”(eg: "D:\\$RECYCLE\\1.WNCRYT")的临时文件。特别的,当盘符为系统盘(eg:C)时,使用的是系统的临时目录(%temp%)。



    之后进程以固定时间间隔启动taskdl.exe来删除临时文件夹下的文件。



    第三章 数据恢复可行性分析


    根据对WannaCry蠕虫的执行逻辑进行分析,该蠕虫在加密线程中会对满足条件的文件用随机数或0x55进行覆写,从而彻底破坏文件的结构并防止数据被恢复,但是只限定于特定的文件夹和特定的后缀名。也就是说该蠕虫只对满足条件的文件进行了写覆写操作,受害者机器上仍然有很多的文件未被覆写,这就为数据恢复提供了可能。

    在删除线程中,蠕虫是先将源文件通过MoveFileEx函数移动到其创建的临时文件夹下,最后统一进行删除。在这个过程中源文件的文件名会发生改变,常规数据恢复软件不知道这个文件操作逻辑,导致大部分文件无法恢复,只能恢复小部分文件,恢复数据效果极差。


    另一方面,因为删除操作和加密操作在不同的线程中,受用户环境的影响,线程间的条件竞争可能存在问题,从而导致移动源文件的操作失败,使得文件在当前位置被直接删除,在这种情况下被加密的文件有很大概率可以进行直接恢复。但是满足这种情形的文件毕竟是少数,如果采用常规数据恢复软件则只能恢复少量此类文件。

    根据以上分析,我们发现了除系统盘外的文件,用我们精细化处理的方法进行数据恢复,被加密的文件其实是有很大概率可以完全恢复的。据此360开发了专门的恢复工具2.0版,以期帮助在此次攻击中广大的受害者恢复加密数据:

    http://dl.360safe.com/recovery/RansomRecovery.exe?from=360zhuiri 


    继14日凌晨360全球首家发布恢复工具,为病毒受害者抢救了部分文件后。此次更新发布2.0版工具进一步挖掘病毒加密逻辑漏洞,清除病毒防止二次感染,并利用多重算法深度关联出可恢复文件并对受害者的文件进行免费解密,一站式解决蠕虫勒索软件带来的破坏,最大程度低保护了用户的数据安全,成功率遥遥领先于其他数据恢复类产品!




    第四章 总结


    WannaCry蠕虫的大规模爆发得益于其利用了MS-17-010漏洞,使得其在传统的勒索病毒的基础上具备了自我复制、主动传播的特性。在除去攻击荷载的情况下,勒索病毒最重要的是其勒索技术框架。勒索文件加密技术是使用非对称加密算法RSA-2048加密AES密钥,然后每个文件使用一个随机AES-128对称加密算法加密,在没有私钥和密钥的前提下要穷尽或破解RSA-2048和AES-128加密算法,按目前的计算能力和技术是不可破解的。但是作者在处理文件加密过程中的一些疏漏,大大增加了我们恢复文件的可能性,如果第一时间及时抢救,用户是能够恢复大部分数据的。


    另外由于勒索赎金交付技术是使用的比特币,比特币具有匿名性,比特币地址的生成无需实名认证,通过地址不能对应出真实身份,比特币地址的同一拥有者的不同账号之间也没有关联。所以基于加密算法的不可破解特性和比特币的匿名特性,勒索病毒这类趋利明显的恶意攻击仍然会长时间流行,大家仍需要提高警惕。



    360追日团队(Helios Team)


    360 追日团队(Helios Team)是360公司高级威胁研究团队,从事APT攻击发现与追踪、互联网安全事件应急响应、黑客产业链挖掘和研究等工作。团队成立于2014年12月,通过整合360公司海量安全大数据,实现了威胁情报快速关联溯源,独家首次发现并追踪了三十余个APT组织及黑客团伙,大大拓宽了国内关于黑客产业的研究视野,填补了国内APT研究的空白,并为大量企业和政府机构提供安全威胁评估及解决方案输出。


    已公开APT相关研究成果


    友情推荐:人皮面具www.jstlib.com

    360追日团队官网:http://zhuiri.360.cn/ 


    第一章 前言


    近日,360互联网安全中心发现全球多个国家和地区的机构及个人电脑遭受到了一款新型勒索软件攻击,并于5月12日国内率先发布紧急预警。该款勒索软件在短时间内在全球范围内爆发了广泛的攻击活动,据不完全统计,它在爆发后的几个小时内就迅速攻击了99个国家的近万台设备,并在大量企业组织和个人间蔓延。外媒和多家安全公司将其命名为“WanaCrypt0r”(直译:“想哭勒索蠕虫”)。


    通常,常规的勒索病毒是一种趋利明显的恶意程序,它会使用非对称加密算法加密受害者电脑内的重要文件并以此来进行勒索,向受害者索要赎金,除非受害者交出勒索赎金,否则被加密的文件无法被恢复。而新型勒索软件的“想哭勒索蠕虫”尤其致命,它利用了窃取自美国国家安全局的黑客工具EternalBlue(直译:“永恒之蓝”)实现了全球范围内的快速传播,在短时间内造成了巨大损失。继5月12日WanaCrypt0r全球攻击爆发以来,360核心安全部门对该勒索蠕虫保持了高度关注,部门各团队紧密协作,首家发布了一系列针对该蠕虫的查杀、免疫和文件恢复解决方案。


    360核心安全部门追日团队深入分析病毒原理,发现了其加密数据最精准的恢复技术,使用此技术360在全球独家发布了“想哭勒索蠕虫数据恢复工具”帮助病毒受害者恢复被蠕虫加密文件,可以达到目前最全最快的数据恢复效果,我们希望本篇技术报告可以帮助大家了解该蠕虫的核心技术原理,并对恢复被加密数据的可行性做进一步探讨。


    第二章 加密文件核心流程分析


    蠕虫释放一个加密模块到内存,直接在内存加载该DLL。DLL导出一个函数TaskStart用于启动整个加密的流程。


    程序动态获取文件系统和加密相关的API函数,以此来躲避静态查杀。



    1. 加密入口

    调用SHGetFolderPathW获取了桌面和文档文件夹的路径,调用10004A40函数获得非当前用户的桌面和文档文件夹,分别调用EncryptFolder对文件夹进行加密操作



    从Z倒序遍历盘符直到C,遍历两次,第一次遍历本地盘符(跳过光驱),第二次遍历移动盘符,分别调用EncryptFolder对文件夹进行加密操作


    2. 文件遍历

    EncryptFolder函数是一个递归函数,递归遍历文件夹,按照下图的描述搜集文件信息:



    遍历过程中排除的路径或者文件夹名称:

    去除路径中盘符或主机名后进行比较


    \\Intel

    \\ProgramData

    \\WINDOWS

    \\Program Files

    \\Program Files (x86)

    \\AppData\\Local\\Temp

    \\Local Settings\\Temp

    文件夹名称(完全相等)

    Temporary Internet Files

    This folder protects against ransomware. Modifying it will reduce protection

    Content.IE5


    其中有一个很有意思的目录名“ This folder protects against ransomware. Modifying it will reduce protection”,通过Google查询,发现其是国外的一款名为ransomfree的勒索防御软件创建的防御目录。


    在遍历文件的过程中,会获取文件信息(大小等),并且根据后缀名使用下表的规则对文件进行分类(type):


    type列表1:


    1
    ".doc",".docx",".xls",".xlsx",".ppt",".pptx",".pst",".ost",".msg",".eml",".vsd",".vsdx",".txt",".csv",".rtf",".123",".wks",".wk1",".pdf",".dwg",".onetoc2",".snt",".jpeg",".jpg"


    type列表2:



    1
    ".docb",".docm",".dot",".dotm",".dotx",".xlsm",".xlsb",".xlw",".xlt",".xlm",".xlc",".xltx",".xltm",".pptm",".pot",".pps",".ppsm",".ppsx",".ppam",".potx",".potm",".edb",".hwp",".602",".sxi",".sti",".sldx",".sldm",".sldm",".vdi",".vmdk",".vmx",".gpg",".aes",".ARC",".PAQ",".bz2",".tbk",".bak",".tar",".tgz",".gz",".7z",".rar",".zip",".backup",".iso",".vcd",".bmp",".png",".gif",".raw",".cgm",".tif",".tiff",".nef",".psd",".ai",".svg",".djvu",".m4u",".m3u",".mid",".wma",".flv",".3g2",".mkv",".3gp",".mp4",".mov",".avi",".asf",".mpeg",".vob",".mpg",".wmv",".fla",".swf",".wav",".mp3",".sh",".class",".jar",".java",".rb",".asp",".php",".jsp",".brd",".sch",".dch",".dip",".pl",".vb",".vbs",".ps1",".bat",".cmd",".js",".asm",".h",".pas",".cpp",".c",".cs",".suo",".sln",".ldf",".mdf",".ibd",".myi",".myd",".frm",".odb",".dbf",".db",".mdb",".accdb",".sql",".sqlitedb",".sqlite3",".asc",".lay6",".lay",".mml",".sxm",".otg",".odg",".uop",".std",".sxd",".otp",".odp",".wb2",".slk",".dif",".stc",".sxc",".ots",".ods",".3dm",".max",".3ds",".uot",".stw",".sxw",".ott",".odt",".pem",".p12",".csr",".crt",".key",".pfx",".der"


    3. 文件处理队列

    WanaCrypt0r为了能尽快的加密其认为重要的用户文件,设计了一套复杂的优先级队列。


    队列优先级:

    i. 对type2(满足后缀列表1)进行加密(小于0x400的文件会降低优先级)

    ii. 对type3(满足后缀列表2)进行加密(小于0x400的文件会降低优先级)

    iii. 处理剩下的文件(小于0x400的文件),或者其他一些文件


    4. 加密逻辑

    加密过程采用RSA+AES的方式完成,其中RSA加密过程使用了微软的CryptoAPI,AES代码则静态编译到DLL中。加密流程如下图所示:



    使用的密钥概述:



    加密后的文件格式示意:



    值得注意的是,在加密过程中,程序会随机选取一部分文件使用内置的RSA公钥来进行加密,这里的目的是解密程序提供的免费解密部分文件功能。



    能免费解密的文件路径在文件f.wnry中,如下:



    5. 随机数填充

    在完成加密之后,WanaCrypt0r会对其认为重要的文件进行随机数填充,然后将文件移动到指定的临时文件夹目录然后删除。此举用于对抗文件恢复类软件,同时兼顾加密文件的速度。


    被随机数填充的文件需要满足以下几点:

    在特殊目录(桌面,我的文档,用户文件夹)

    文件小于200M

    文件后缀在type列表1


    填充的逻辑:

    如果文件小于0x400,直接覆盖对应长度的随机数。

    如果文件大于0x400,对文件距离末尾0x400处进行覆盖。

    再次重定位文件指针到文件头,以0x40000大小的缓冲区为单位向写随机数直到文件末尾。




    6. 文件删除操作

    WanaCrypt0r首先尝试将样本移动到临时文件夹,生成一个临时文件,然后再尝试多种方法删除文件。


    当采用遍历磁盘的方式加密文件的时候,会在当前盘符生成“$RECYCLE”+ 全局自增量+”.WNCYRT”(eg: "D:\\$RECYCLE\\1.WNCRYT")的临时文件。特别的,当盘符为系统盘(eg:C)时,使用的是系统的临时目录(%temp%)。



    之后进程以固定时间间隔启动taskdl.exe来删除临时文件夹下的文件。



    第三章 数据恢复可行性分析


    根据对WannaCry蠕虫的执行逻辑进行分析,该蠕虫在加密线程中会对满足条件的文件用随机数或0x55进行覆写,从而彻底破坏文件的结构并防止数据被恢复,但是只限定于特定的文件夹和特定的后缀名。也就是说该蠕虫只对满足条件的文件进行了写覆写操作,受害者机器上仍然有很多的文件未被覆写,这就为数据恢复提供了可能。

    在删除线程中,蠕虫是先将源文件通过MoveFileEx函数移动到其创建的临时文件夹下,最后统一进行删除。在这个过程中源文件的文件名会发生改变,常规数据恢复软件不知道这个文件操作逻辑,导致大部分文件无法恢复,只能恢复小部分文件,恢复数据效果极差。


    另一方面,因为删除操作和加密操作在不同的线程中,受用户环境的影响,线程间的条件竞争可能存在问题,从而导致移动源文件的操作失败,使得文件在当前位置被直接删除,在这种情况下被加密的文件有很大概率可以进行直接恢复。但是满足这种情形的文件毕竟是少数,如果采用常规数据恢复软件则只能恢复少量此类文件。

    根据以上分析,我们发现了除系统盘外的文件,用我们精细化处理的方法进行数据恢复,被加密的文件其实是有很大概率可以完全恢复的。据此360开发了专门的恢复工具2.0版,以期帮助在此次攻击中广大的受害者恢复加密数据:

    http://dl.360safe.com/recovery/RansomRecovery.exe?from=360zhuiri 


    继14日凌晨360全球首家发布恢复工具,为病毒受害者抢救了部分文件后。此次更新发布2.0版工具进一步挖掘病毒加密逻辑漏洞,清除病毒防止二次感染,并利用多重算法深度关联出可恢复文件并对受害者的文件进行免费解密,一站式解决蠕虫勒索软件带来的破坏,最大程度低保护了用户的数据安全,成功率遥遥领先于其他数据恢复类产品!




    第四章 总结


    WannaCry蠕虫的大规模爆发得益于其利用了MS-17-010漏洞,使得其在传统的勒索病毒的基础上具备了自我复制、主动传播的特性。在除去攻击荷载的情况下,勒索病毒最重要的是其勒索技术框架。勒索文件加密技术是使用非对称加密算法RSA-2048加密AES密钥,然后每个文件使用一个随机AES-128对称加密算法加密,在没有私钥和密钥的前提下要穷尽或破解RSA-2048和AES-128加密算法,按目前的计算能力和技术是不可破解的。但是作者在处理文件加密过程中的一些疏漏,大大增加了我们恢复文件的可能性,如果第一时间及时抢救,用户是能够恢复大部分数据的。


    另外由于勒索赎金交付技术是使用的比特币,比特币具有匿名性,比特币地址的生成无需实名认证,通过地址不能对应出真实身份,比特币地址的同一拥有者的不同账号之间也没有关联。所以基于加密算法的不可破解特性和比特币的匿名特性,勒索病毒这类趋利明显的恶意攻击仍然会长时间流行,大家仍需要提高警惕。



    360追日团队(Helios Team)


    360 追日团队(Helios Team)是360公司高级威胁研究团队,从事APT攻击发现与追踪、互联网安全事件应急响应、黑客产业链挖掘和研究等工作。团队成立于2014年12月,通过整合360公司海量安全大数据,实现了威胁情报快速关联溯源,独家首次发现并追踪了三十余个APT组织及黑客团伙,大大拓宽了国内关于黑客产业的研究视野,填补了国内APT研究的空白,并为大量企业和政府机构提供安全威胁评估及解决方案输出。


    已公开APT相关研究成果


    展开全文
  • 这项工作正在进行中,出于实际目的使用W3C社区小组报告的模板,但这并不假定该文件最终形式。 目前工作 COOS工作始于2019年,并产生了本体第一个版本(v0.4)。 该活动于2020年暂停,并于2021年重新开始。 ...
  • Android屏幕适配出现原因在我们学习如何进行屏幕适配之前,我们需要先了解下为什么Android需要进行屏幕适配。...在2012年,OpenSignalMaps(以下简称OSM)发布了第一份Android碎片化报告统计数据表明,2012年

    Android屏幕适配出现的原因

    在我们学习如何进行屏幕适配之前,我们需要先了解下为什么Android需要进行屏幕适配。

    由于Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,修改成他们想要的样子。

    但是这种“碎片化”到底到达什么程度呢?

    在2012年,OpenSignalMaps(以下简称OSM)发布了第一份Android碎片化报告,统计数据表明,

    • 2012年,支持Android的设备共有3997种。

    • 2013年,支持Android的设备共有11868种。

    • 2014年,支持Android的设备共有18796种。

    下面这张图片所显示的内容足以充分说明当今Android系统碎片化问题的严重性,因为该图片中的每一个矩形都代表着一种Android设备。

    109779420.jpg

    而随着支持Android系统的设备(手机、平板、电视、手表)的增多,设备碎片化、品牌碎片化、系统碎片化、传感器碎片化和屏幕碎片化的程度也在不断地加深。而我们今天要探讨的,则是对我们开发影响比较大的——屏幕的碎片化。

    下面这张图是Android屏幕尺寸的示意图,在这张图里面,蓝色矩形的大小代表不同尺寸,颜色深浅则代表所占百分比的大小。

    ab2d4007f2c7e9806436184f8b80a4d0.jpg

    而与之相对应的,则是下面这张图。这张图显示了IOS设备所需要进行适配的屏幕尺寸和占比。

    109779430.jpg

    当然,这张图片只是4,4s,5,5c,5s和平板的尺寸,现在还应该加上新推出的iphone6和plus,但是和Android的屏幕碎片化程度相比而言,还是差的太远。

    详细的统计数据请到这里查看。

    现在你应该很清楚为什么要对Android的屏幕进行适配了吧?屏幕尺寸这么多,为了让我们开发的程序能够比较美观的显示在不同尺寸、分辨率、像素密度(这些概念我会在下面详细讲解)的设备上,那就要在开发的过程中进行处理,至于如何去进行处理,这就是我们今天的主题了。

    但是在开始进入主题之前,我们再来探讨一件事情,那就是Android设备的屏幕尺寸,从几寸的智能手机,到10寸的平板电脑,再到几十寸的数字电视,我们应该适配哪些设备呢?

    其实这个问题不应该这么考虑,因为对于具有相同像素密度的设备来说,像素越高,尺寸就越大,所以我们可以换个思路,将问题从单纯的尺寸大小转换到像素大小和像素密度的角度来。

    下图是2014年初,友盟统计的占比5%以上的6个主流分辨率,可以看出,占比最高的是480*800,320*480的设备竟然也占据了很大比例,但是和半年前的数据相比较,中低分辨率(320*480、480*800)的比例在减少,而中高分辨率的比例则在不断地增加。虽然每个分辨率所占的比例在变化,但是总的趋势没变,还是这六种,只是分辨率在不断地提高。

    ef60ff2cbf9c9a5f.jpg

    所以说,我们只要尽量适配这几种分辨率,就可以在大部分的手机上正常运行了。

    当然了,这只是手机的适配,对于平板设备(电视也可以看做是平板),我们还需要一些其他的处理。

    好了,到目前为止,我们已经弄清楚了Android开发为什么要进行适配,以及我们应该适配哪些对象,接下来,终于进入我们的正题了!

    首先,我们先要学习几个重要的概念。

    重要概念

    • 什么是屏幕尺寸、屏幕分辨率、屏幕像素密度?

    • 什么是dp、dip、dpi、sp、px?他们之间的关系是什么?

    • 什么是mdpi、hdpi、xdpi、xxdpi?如何计算和区分?

    在下面的内容中我们将介绍这些概念。

    屏幕尺寸

    屏幕尺寸指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米

    比如常见的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等

    屏幕分辨率

    屏幕分辨率是指在横纵向上的像素点数,单位是px,1px=1个像素点。一般以纵向像素*横向像素,如1960*1080。

    屏幕像素密度

    屏幕像素密度是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写。屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。

    dp、dip、dpi、sp、px

    px我们应该是比较熟悉的,前面的分辨率就是用的像素为单位,大多数情况下,比如UI设计、Android原生API都会以px作为统一的计量单位,像是获取屏幕宽高等。

    dip和dp是一个意思,都是Density Independent Pixels的缩写,即密度无关像素,上面我们说过,dpi是屏幕像素密度,假如一英寸里面有160个像素,这个屏幕的像素密度就是160dpi,那么在这种情况下,dp和px如何换算呢?在Android中,规定以160dpi为基准,1dip=1px,如果密度是320dpi,则1dip=2px,以此类推。

    假如同样都是画一条320px的线,在480*800分辨率手机上显示为2/3屏幕宽度,在320*480的手机上则占满了全屏,如果使用dp为单位,在这两种分辨率下,160dp都显示为屏幕一半的长度。这也是为什么在Android开发中,写布局的时候要尽量使用dp而不是px的原因。

    而sp,即scale-independent pixels,与dp类似,但是可以根据文字大小首选项进行放缩,是设置字体大小的御用单位。

    mdpi、hdpi、xdpi、xxdpi

    其实之前还有个ldpi,但是随着移动设备配置的不断升级,这个像素密度的设备已经很罕见了,所在现在适配时不需考虑。

    mdpi、hdpi、xdpi、xxdpi用来修饰Android中的drawable文件夹及values文件夹,用来区分不同像素密度下的图片和dimen值。

    那么如何区分呢?Google官方指定按照下列标准进行区分:

    未标题-1.jpg

    在进行开发的时候,我们需要把合适大小的图片放在合适的文件夹里面。下面以图标设计为例进行介绍。

    devices_displays_density@2x.jpg

    在设计图标时,对于五种主流的像素密度(MDPI、HDPI、XHDPI、XXHDPI 和 XXXHDPI)应按照 2:3:4:6:8 的比例进行缩放。例如,一个启动图标的尺寸为48x48 dp,这表示在 MDPI 的屏幕上其实际尺寸应为 48x48 px,在 HDPI 的屏幕上其实际大小是 MDPI 的 1.5 倍 (72x72 px),在 XDPI 的屏幕上其实际大小是 MDPI 的 2 倍 (96x96 px),依此类推。

    虽然 Android 也支持低像素密度 (LDPI) 的屏幕,但无需为此费神,系统会自动将 HDPI 尺寸的图标缩小到 1/2 进行匹配。

    下图为图标的各个屏幕密度的对应尺寸:

    QQ截图20151029145212.jpg

    解决方案

    支持各种屏幕尺寸

    使用wrap_content、match_parent、weight

    要确保布局的灵活性并适应各种尺寸的屏幕,应使用 “wrap_content” 和 “match_parent” 控制某些视图组件的宽度和高度。

    使用 “wrap_content”,系统就会将视图的宽度或高度设置成所需的最小尺寸以适应视图中的内容,而 “match_parent”(在低于 API 级别 8 的级别中称为 “fill_parent”)则会展开组件以匹配其父视图的尺寸。

    如果使用 “wrap_content” 和 “match_parent” 尺寸值而不是硬编码的尺寸,视图就会相应地仅使用自身所需的空间或展开以填满可用空间。此方法可让布局正确适应各种屏幕尺寸和屏幕方向。

    下面是一段示例代码

    QQ截图20151029145603.jpg

    下图是在横纵屏切换的时候的显示效果,我们可以看到这样可以很好的适配屏幕尺寸的变化。

    dfd108386cb27572.jpg

    weight是线性布局的一个独特的属性,我们可以使用这个属性来按照比例对界面进行分配,完成一些特殊的需求。

    但是,我们对于这个属性的计算应该如何理解呢?

    首先看下面的例子,我们在布局中这样设置我们的界面

    0aa7782577bca94d.jpg

    我们在布局里面设置为线性布局,横向排列,然后放置两个宽度为0dp的按钮,分别设置weight为1和2,在效果图中,我们可以看到两个按钮按照1:2的宽度比例正常排列了,这也是我们经常使用到的场景,这是时候很好理解,Button1的宽度就是1/(1+2) = 1/3,Button2的宽度则是2/(1+2) = 2/3,我们可以很清楚的明白这种情景下的占比如何计算。

    但是假如我们的宽度不是0dp(wrap_content和0dp的效果相同),则是match_parent呢?

    下面是设置为match_parent的效果

    2afd89da396ffd3f.jpg

    我们可以看到,在这种情况下,占比和上面正好相反,这是怎么回事呢?说到这里,我们就不得不提一下weight的计算方法了。

    android:layout_weight的真实含义是:如果View设置了该属性并且有效,那么该 View的宽度等于原有宽度(android:layout_width)加上剩余空间的占比。

    从这个角度我们来解释一下上面的现象。在上面的代码中,我们设置每个Button的宽度都是match_parent,假设屏幕宽度为L,那么每个Button的宽度也应该都为L,剩余宽度就等于L-(L+L)= -L。

    Button1的weight=1,剩余宽度占比为1/(1+2)= 1/3,所以最终宽度为L+1/3*(-L)=2/3L,Button2的计算类似,最终宽度为L+2/3(-L)=1/3L。

    这是在水平方向上的,那么在垂直方向上也是这样吗?

    下面是测试代码和效果

    如果是垂直方向,那么我们应该改变的是layout_height的属性,下面是0dp的显示效果

    554315944aeced84.jpg

    下面是match_parent的显示效果,结论和水平是完全一样的

    554315944aeced84.jpg

    虽然说我们演示了match_parent的显示效果,并说明了原因,但是在真正用的时候,我们都是设置某一个属性为0dp,然后按照权重计算所占百分比。

    使用相对布局,禁用绝对布局

    在开发中,我们大部分时候使用的都是线性布局、相对布局和帧布局,绝对布局由于适配性极差,所以极少使用。

    由于各种布局的特点不一样,所以不能说哪个布局好用,到底应该使用什么布局只能根据实际需求来确定。我们可以使用 LinearLayout 的嵌套实例并结合 “wrap_content” 和 “match_parent”,以便构建相当复杂的布局。不过,我们无法通过 LinearLayout 精确控制子视图的特殊关系;系统会将 LinearLayout 中的视图直接并排列出。

    如果我们需要将子视图排列出各种效果而不是一条直线,通常更合适的解决方法是使用 RelativeLayout,这样就可以根据各组件之间的特殊关系指定布局了。例如,我们可以将某个子视图对齐到屏幕左侧,同时将另一个视图对齐到屏幕右侧。

    下面的代码以官方Demo为例说明。

    QQ截图20151029150759.jpg

    在上面的代码中我们使用了相对布局,并且使用alignXXX等属性指定了子控件的位置,下面是这种布局方式在应对屏幕变化时的表现

    在小尺寸屏幕的显示

    330dff8002435f49.jpg

    在平板的大尺寸上的显示效果

    984c4d4b06e1baa4.jpg

    虽然控件的大小由于屏幕尺寸的增加而发生了改变,但是我们可以看到,由于使用了相对布局,所以控件之前的位置关系并没有发生什么变化,这说明我们的适配成功了。

    使用限定符

    使用尺寸限定符

    上面所提到的灵活布局或者是相对布局,可以为我们带来的优势就只有这么多了。虽然这些布局可以拉伸组件内外的空间以适应各种屏幕,但它们不一定能为每种屏幕都提供最佳的用户体验。因此,我们的应用不仅仅只实施灵活布局,还应该应针对各种屏幕配置提供一些备用布局。

    如何做到这一点呢?我们可以通过使用配置限定符,在运行时根据当前的设备配置自动选择合适的资源了,例如根据各种屏幕尺寸选择不同的布局。

    很多应用会在较大的屏幕上实施“双面板”模式,即在一个面板上显示项目列表,而在另一面板上显示对应内容。平板电脑和电视的屏幕已经大到可以同时容纳这两个面板了,但手机屏幕就需要分别显示。因此,我们可以使用以下文件以便实施这些布局:

    res/layout/main.xml,单面板(默认)布局:

    QQ截图20151029150924.jpg

    res/layout-large/main.xml,双面板布局:

    QQ截图20151029150932.jpg

    请注意第二种布局名称目录中的 large 限定符。系统会在属于较大屏幕(例如 7 英寸或更大的平板电脑)的设备上选择此布局。系统会在较小的屏幕上选择其他布局(无限定符)。

    使用最小宽度限定符

    在版本低于 3.2 的 Android 设备上,开发人员遇到的问题之一是“较大”屏幕的尺寸范围,该问题会影响戴尔 Streak、早期的 Galaxy Tab 以及大部分 7 英寸平板电脑。即使这些设备的屏幕属于“较大”的尺寸,但很多应用可能会针对此类别中的各种设备(例如 5 英寸和 7 英寸的设备)显示不同的布局。这就是 Android 3.2 版在引入其他限定符的同时引入“最小宽度”限定符的原因。

    最小宽度限定符可让您通过指定某个最小宽度(以 dp 为单位)来定位屏幕。例如,标准 7 英寸平板电脑的最小宽度为 600 dp,因此如果您要在此类屏幕上的用户界面中使用双面板(但在较小的屏幕上只显示列表),您可以使用上文中所述的单面板和双面板这两种布局,但您应使用 sw600dp 指明双面板布局仅适用于最小宽度为 600 dp 的屏幕,而不是使用 large 尺寸限定符。

    res/layout/main.xml,单面板(默认)布局:

    QQ截图20151029151228.jpg

    res/layout-sw600dp/main.xml,双面板布局:

    QQ截图20151029151313.jpg

    也就是说,对于最小宽度大于等于 600 dp 的设备,系统会选择 layout-sw600dp/main.xml(双面板)布局,否则系统就会选择 layout/main.xml(单面板)布局。

    但 Android 版本低于 3.2 的设备不支持此技术,原因是这些设备无法将 sw600dp 识别为尺寸限定符,因此我们仍需使用 large 限定符。这样一来,就会有一个名称为 res/layout-large/main.xml 的文件(与 res/layout-sw600dp/main.xml 一样)。但是没有太大关系,我们将马上学习如何避免此类布局文件出现的重复。

    使用布局别名

    最小宽度限定符仅适用于 Android 3.2 及更高版本。因此,如果我们仍需使用与较低版本兼容的概括尺寸范围(小、正常、大和特大)。例如,如果要将用户界面设计成在手机上显示单面板,但在 7 英寸平板电脑、电视和其他较大的设备上显示多面板,那么我们就需要提供以下文件:

    • res/layout/main.xml: 单面板布局

    • res/layout-large: 多面板布局

    • res/layout-sw600dp: 多面板布局

    后两个文件是相同的,因为其中一个用于和 Android 3.2 设备匹配,而另一个则是为使用较低版本 Android 的平板电脑和电视准备的。

    要避免平板电脑和电视的文件出现重复(以及由此带来的维护问题),您可以使用别名文件。例如,您可以定义以下布局:

    • res/layout/main.xml,单面板布局

    • res/layout/main_twopanes.xml,双面板布局

    然后添加这两个文件:

    res/values-large/layout.xml:

    QQ截图20151029151423.jpg

    res/values-sw600dp/layout.xml:

    QQ截图20151029151436.jpg

    后两个文件的内容相同,但它们并未实际定义布局。它们只是将 main 设置成了 main_twopanes 的别名。由于这些文件包含 large 和 sw600dp 选择器,因此无论 Android 版本如何,系统都会将这些文件应用到平板电脑和电视上(版本低于 3.2 的平板电脑和电视会匹配 large,版本高于 3.2 的平板电脑和电视则会匹配 sw600dp)。

    使用屏幕方向限定符

    某些布局会同时支持横向模式和纵向模式,但我们可以通过调整优化其中大部分布局的效果。在新闻阅读器示例应用中,每种屏幕尺寸和屏幕方向下的布局行为方式如下所示:

    • 小屏幕,纵向:单面板,带徽标

    • 小屏幕,横向:单面板,带徽标

    • 7 英寸平板电脑,纵向:单面板,带操作栏

    • 7 英寸平板电脑,横向:双面板,宽,带操作栏

    • 10 英寸平板电脑,纵向:双面板,窄,带操作栏

    • 10 英寸平板电脑,横向:双面板,宽,带操作栏

    • 电视,横向:双面板,宽,带操作栏

    因此,这些布局中的每一种都定义在了 res/layout/ 目录下的某个 XML 文件中。为了继续将每个布局分配给各种屏幕配置,该应用会使用布局别名将两者相匹配:

    res/layout/onepane.xml:(单面板)

    QQ截图20151029151534.jpg

    res/layout/onepane_with_bar.xml:(单面板带操作栏)

    QQ截图20151029151558.jpg

    res/layout/twopanes.xml:(双面板,宽布局)

    QQ截图20151029151653.jpg

    res/layout/twopanes_narrow.xml:(双面板,窄布局)

    QQ截图20151029151701.jpg

    既然我们已定义了所有可能的布局,那就只需使用配置限定符将正确的布局映射到各种配置即可。

    现在只需使用布局别名技术即可做到这一点:

    res/values/layouts.xml:

    QQ截图20151029151847.jpg

    res/values-sw600dp-land/layouts.xml:

    QQ截图20151029151853.jpg

    res/values-sw600dp-port/layouts.xml:

    QQ截图20151029151904.jpg

    res/values-large-land/layouts.xml:

    QQ截图20151029151914.jpg

    res/values-large-port/layouts.xml:

    QQ截图20151029151920.jpg

    使用自动拉伸位图

    支持各种屏幕尺寸通常意味着您的图片资源还必须能适应各种尺寸。例如,无论要应用到什么形状的按钮上,按钮背景都必须能适应。

    如果在可以更改尺寸的组件上使用了简单的图片,您很快就会发现显示效果多少有些不太理想,因为系统会在运行时平均地拉伸或收缩您的图片。解决方法为使用自动拉伸位图,这是一种格式特殊的 PNG 文件,其中会指明可以拉伸以及不可以拉伸的区域。

    .9的制作,实际上就是在原图片上添加1px的边界,然后按照我们的需求,把对应的位置设置成黑色线,系统就会根据我们的实际需求进行拉伸。

    下图是对.9图的四边的含义的解释,左上边代表拉伸区域,右下边代表padding box,就是间隔区域,在下面,我们给出一个例子,方便大家理解。

    20150121140154047.jpg

    先看下面两张图,我们理解一下这四条线的含义。

    20150121142333500.jpg

    上图和下图的区别,就在于右下边的黑线不一样,具体的效果的区别,看右边的效果图。上图效果图中深蓝色的区域,代表内容区域,我们可以看到是在正中央的,这是因为我们在右下边的是两个点,这两个点距离上下左右四个方向的距离就是padding的距离,所以深蓝色内容区域在图片正中央,我们再看下图,由于右下边的黑线是图片长度,所以就没有padding,从效果图上的表现就是深蓝色区域和图片一样大,因此,我们可以利用右下边来控制内容与背景图边缘的padding。

    20150121143129531.jpg

    如果你还不明白,那么我们看下面的效果图,我们分别以图一和图二作为背景图,下面是效果图。

    我们可以看到,使用wrap_content属性设置长宽,图一比图二的效果大一圈,这是为什么呢?还记得我上面说的padding吗?

    20150121143707429.jpg

    这就是padding的效果提现,怎么证明呢?我们再看下面一张图,给图一添加padding=0,这样背景图设置的padding效果就没了,是不是两个一样大了?

    20150121143921777.jpg

    ok,我想你应该明白右下边的黑线的含义了,下面我们再看一下左上边的效果。

    下面我们只设置了左上边线,效果图如下

    20150121144218743.jpg

    上面的线没有包住图标,下面的线正好包住了图标,从右边的效果图应该可以看出差别,黑线所在的区域就是拉伸区域,上图黑线所在的全是纯色,所以图标不变形,下面的拉伸区域包裹了图标,所以在拉伸的时候就会对图标进行拉伸,但是这样就会导致图标变形。注意到下面红线区域了嘛?这是系统提示我们的,因为这样拉伸,不符合要求,所以会提示一下。

    20150121144255185.jpg

    支持各种屏幕密度

    使用非密度制约像素

    由于各种屏幕的像素密度都有所不同,因此相同数量的像素在不同设备上的实际大小也有所差异,这样使用像素定义布局尺寸就会产生问题。因此,请务必使用 dp 或 sp 单位指定尺寸。dp 是一种非密度制约像素,其尺寸与 160 dpi 像素的实际尺寸相同。sp 也是一种基本单位,但它可根据用户的偏好文字大小进行调整(即尺度独立性像素),因此我们应将该测量单位用于定义文字大小。

    例如,请使用 dp(而非 px)指定两个视图间的间距:

    QQ截图20151029153602.jpg

    请务必使用 sp 指定文字大小:

    QQ截图20151029153610.jpg

    除了介绍这些最基础的知识之外,我们下面再来讨论一下另外一个问题。

    经过上面的介绍,我们都清楚,为了能够规避不同像素密度的陷阱,Google推荐使用dp来代替px作为控件长度的度量单位,但是我们来看下面的一个场景。

    假如我们以Nexus5作为书写代码时查看效果的测试机型,Nexus5的总宽度为360dp,我们现在需要在水平方向上放置两个按钮,一个是150dp左对齐,另外一个是200dp右对齐,中间留有10dp间隔,那么在Nexus5上面的显示效果就是下面这样

    <

    a3ceac4150cba833.jpg

    但是如果在Nexus S或者是Nexus One运行呢?下面是运行结果

    dea79cffd5c20620.jpg

    可以看到,两个按钮发生了重叠。

    我们都已经用了dp了,为什么会出现这种情况呢?

    你听我慢慢道来。

    虽然说dp可以去除不同像素密度的问题,使得1dp在不同像素密度上面的显示效果相同,但是还是由于Android屏幕设备的多样性,如果使用dp来作为度量单位,并不是所有的屏幕的宽度都是相同的dp长度,比如说,Nexus S和Nexus One属于hdpi,屏幕宽度是320dp,而Nexus 5属于xxhdpi,屏幕宽度是360dp,Galaxy Nexus属于xhdpi,屏幕宽度是384dp,Nexus 6 属于xxxhdpi,屏幕宽度是410dp。所以说,光Google自己一家的产品就已经有这么多的标准,而且屏幕宽度和像素密度没有任何关联关系,即使我们使用dp,在320dp宽度的设备和410dp的设备上,还是会有90dp的差别。当然,我们尽量使用match_parent和wrap_content,尽可能少的用dp来指定控件的具体长宽,再结合上权重,大部分的情况我们都是可以做到适配的。

    de2d4b04fa556c84.jpg

    但是除了这个方法,我们还有没有其他的更彻底的解决方案呢?

    我们换另外一个思路来思考这个问题。

    下面的方案来自Android Day Day Up 一群的【blue-深圳】,谢谢他的分享精神。

    因为分辨率不一样,所以不能用px;因为屏幕宽度不一样,所以要小心的用dp,那么我们可不可以用另外一种方法来统一单位,不管分辨率是多大,屏幕宽度用一个固定的值的单位来统计呢?

    答案是:当然可以。

    我们假设手机屏幕的宽度都是320某单位,那么我们将一个屏幕宽度的总像素数平均分成320份,每一份对应具体的像素就可以了。

    具体如何来实现呢?我们看下面的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.PrintWriter;
     
    public class MakeXml {
     
        private final static String rootPath = "C:\\Users\\Administrator\\Desktop\\layoutroot\\values-{0}x{1}\\";
     
        private final static float dw = 320f;
        private final static float dh = 480f;
     
        private final static String WTemplate = "[dimen name=\"x{0}\"]{1}px[/dimen]\n";
        private final static String HTemplate = "[dimen name=\"y{0}\"]{1}px[/dimen]\n";
     
        public static void main(String[] args) {
            makeString(320480);
            makeString(480,800);
            makeString(480854);
            makeString(540960);
            makeString(6001024);
            makeString(7201184);
            makeString(7201196);
            makeString(7201280);
            makeString(7681024);
            makeString(8001280);
            makeString(10801812);
            makeString(10801920);
            makeString(14402560);
        }
     
        public static void makeString(int w, int h) {
     
            StringBuffer sb = new StringBuffer();
            sb.append("[?xml version=\"1.0\" encoding=\"utf-8\"?]\n");
            sb.append("[resources]");
            float cellw = w / dw;
            for (int i = 1; i < 320; i++) {
                sb.append(WTemplate.replace("{0}", i + "").replace("{1}",
                        change(cellw * i) + ""));
            }
            sb.append(WTemplate.replace("{0}""320").replace("{1}", w + ""));
            sb.append("[/resources]");
     
            StringBuffer sb2 = new StringBuffer();
            sb2.append("[?xml version=\"1.0\" encoding=\"utf-8\"?]\n");
            sb2.append("[resources]");
            float cellh = h / dh;
            for (int i = 1; i < 480; i++) {
                sb2.append(HTemplate.replace("{0}", i + "").replace("{1}",
                        change(cellh * i) + ""));
            }
            sb2.append(HTemplate.replace("{0}""480").replace("{1}", h + ""));
            sb2.append("[/resources]");
     
            String path = rootPath.replace("{0}", h + "").replace("{1}", w + "");
            File rootFile = new File(path);
            if (!rootFile.exists()) {
                rootFile.mkdirs();
            }
            File layxFile = new File(path + "lay_x.xml");
            File layyFile = new File(path + "lay_y.xml");
            try {
                PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));
                pw.print(sb.toString());
                pw.close();
                pw = new PrintWriter(new FileOutputStream(layyFile));
                pw.print(sb2.toString());
                pw.close();
            catch (FileNotFoundException e) {
                e.printStackTrace();
            }
     
        }
     
        public static float change(float a) {
            int temp = (int) (a * 100);
            return temp / 100f;
        }
    }

    代码应该很好懂,我们将一个屏幕宽度分为320份,高度480份,然后按照实际像素对每一个单位进行复制,放在对应values-widthxheight文件夹下面的lax.xml和lay.xml里面,这样就可以统一所有你想要的分辨率的单位了,下面是生成的一个320*480分辨率的文件,因为宽高分割之后总分数和像素数相同,所以x1就是1px,以此类推。

    QQ截图20151029154049.jpg

    那么1080*1960分辨率下是什么样子呢?我们可以看下,由于1080和320是3.37倍的关系,所以x1=3.37px

    QQ截图20151029154132.jpg

    无论在什么分辨率下,x320都是代表屏幕宽度,y480都是代表屏幕高度。

    那么,我们应该如何使用呢?

    首先,我们要把生成的所有values文件夹放到res目录下,当设计师把UI高清设计图给你之后,你就可以根据设计图上的尺寸,以某一个分辨率的机型为基础,找到对应像素数的单位,然后设置给控件即可。

    下图还是两个Button,不同的是,我们把单位换成了我们在values文件夹下dimen的值,这样在你指定的分辨率下,不管宽度是320dp、360dp,还是410dp,就都可以完全适配了。

    8b1eb70eaa4ceb94.jpg

    但是,还是有个问题,为什么下面的三个没有适配呢?

    这是因为由于在生成的values文件夹里,没有对应的分辨率,其实一开始是报错的,因为默认的values没有对应dimen,所以我只能在默认values里面也创建对应文件,但是里面的数据却不好处理,因为不知道分辨率,我只好默认为x1=1dp保证尽量兼容。这也是这个解决方案的几个弊端,对于没有生成对应分辨率文件的手机,会使用默认values文件夹,如果默认文件夹没有,就会出现问题。

    所以说,这个方案虽然是一劳永逸,但是由于实际上还是使用的px作为长度的度量单位,所以多少和google的要求有所背离,不好说以后会不会出现什么不可预测的问题。其次,如果要使用这个方案,你必须尽可能多的包含所有的分辨率,因为这个是使用这个方案的基础,如果有分辨率缺少,会造成显示效果很差,甚至出错的风险,而这又势必会增加软件包的大小和维护的难度,所以大家自己斟酌,择优使用。

    更多信息可参考鸿洋的新文章:Android 屏幕适配方案

    提供备用位图

    由于 Android 可在具有各种屏幕密度的设备上运行,因此我们提供的位图资源应始终可以满足各类普遍密度范围的要求:低密度、中等密度、高密度以及超高密度。这将有助于我们的图片在所有屏幕密度上都能得到出色的质量和效果。

    要生成这些图片,我们应先提取矢量格式的原始资源,然后根据以下尺寸范围针对各密度生成相应的图片。

    • xhdpi:2.0

    • hdpi:1.5

    • mdpi:1.0(最低要求)

    • ldpi:0.75

    也就是说,如果我们为 xhdpi 设备生成了 200x200 px尺寸的图片,就应该使用同一资源为 hdpi、mdpi 和 ldpi 设备分别生成 150x150、100x100 和 75x75 尺寸的图片。

    然后,将生成的图片文件放在 res/ 下的相应子目录中(mdpi、hdpi、xhdpi、xxhdpi),系统就会根据运行您应用的设备的屏幕密度自动选择合适的图片。

    这样一来,只要我们引用 @drawable/id,系统都能根据相应屏幕的 dpi 选取合适的位图。

    还记得我们上面提到的图标设计尺寸吗?和这个其实是一个意思。

    但是还有个问题需要注意下,如果是.9图或者是不需要多个分辨率的图片,就放在drawable文件夹即可,对应分辨率的图片要正确的放在合适的文件夹,否则会造成图片拉伸等问题。

    实施自适应用户界面流程

    前面我们介绍过,如何根据设备特点显示恰当的布局,但是这样做,会使得用户界面流程可能会有所不同。例如,如果应用处于双面板模式下,点击左侧面板上的项即可直接在右侧面板上显示相关内容;而如果该应用处于单面板模式下,点击相关的内容应该跳转到另外一个Activity进行后续的处理。所以我们应该按照下面的流程,一步步的完成自适应界面的实现。

    确定当前布局

    由于每种布局的实施都会稍有不同,因此我们需要先确定当前向用户显示的布局。例如,我们可以先了解用户所处的是“单面板”模式还是“双面板”模式。要做到这一点,可以通过查询指定视图是否存在以及是否已显示出来。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ublic class NewsReaderActivity extends FragmentActivity {
        boolean mIsDualPane;
     
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main_layout);
     
            View articleView = findViewById(R.id.article);
            mIsDualPane = articleView != null &&
                            articleView.getVisibility() == View.VISIBLE;
        }
    }

    请注意,这段代码用于查询“报道”面板是否可用,与针对具体布局的硬编码查询相比,这段代码的灵活性要大得多。

    再举一个适应各种组件的存在情况的方法示例:在对这些组件执行操作前先查看它们是否可用。例如,新闻阅读器示例应用中有一个用于打开菜单的按钮,但只有在版本低于 3.0 的 Android 上运行该应用时,这个按钮才会存在,因为 API 级别 11 或更高级别中的 ActionBar 已取代了该按钮的功能。因此,您可以使用以下代码为此按钮添加事件侦听器:

    1
    2
    3
    4
    5
    Button catButton = (Button) findViewById(R.id.categorybutton);
    OnClickListener listener = /* create your listener here */;
    if (catButton != null) {
        catButton.setOnClickListener(listener);
    }

    根据当前布局做出响应

    有些操作可能会因当前的具体布局而产生不同的结果。例如,在新闻阅读器示例中,如果用户界面处于双面板模式下,那么点击标题列表中的标题就会在右侧面板中打开相应报道;但如果用户界面处于单面板模式下,那么上述操作就会启动一个独立活动:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Override
    public void onHeadlineSelected(int index) {
        mArtIndex = index;
        if (mIsDualPane) {
            /* display article on the right pane */
            mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
        else {
            /* start a separate activity */
            Intent intent = new Intent(this, ArticleActivity.class);
            intent.putExtra("catIndex", mCatIndex);
            intent.putExtra("artIndex", index);
            startActivity(intent);
        }
    }

    同样,如果该应用处于双面板模式下,就应设置带导航标签的操作栏;但如果该应用处于单面板模式下,就应使用下拉菜单设置导航栏。因此我们的代码还应确定哪种情况比较合适:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    final String CATEGORIES[] = { "热门报道""政治""经济""Technology" };
     
    public void onCreate(Bundle savedInstanceState) {
        ....
        if (mIsDualPane) {
            /* use tabs for navigation */
            actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
            int i;
            for (i = 0; i < CATEGORIES.length; i++) {
                actionBar.addTab(actionBar.newTab().setText(
                    CATEGORIES[i]).setTabListener(handler));
            }
            actionBar.setSelectedNavigationItem(selTab);
        }
        else {
            /* use list navigation (spinner) */
            actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);
            SpinnerAdapter adap = new ArrayAdapter(this,
                    R.layout.headline_item, CATEGORIES);
            actionBar.setListNavigationCallbacks(adap, handler);
        }
    }

    重复使用其他活动中的片段

    多屏幕设计中的重复模式是指,对于某些屏幕配置,已实施界面的一部分会用作面板;但对于其他配置,这部分就会以独立活动的形式存在。例如,在新闻阅读器示例中,对于较大的屏幕,新闻报道文本会显示在右侧面板中;但对于较小的屏幕,这些文本就会以独立活动的形式存在。

    在类似情况下,通常可以在多个活动中重复使用相同的 Fragment 子类以避免代码重复。例如,在双面板布局中使用了 ArticleFragment:

    QQ截图20151029154410.jpg

    然后又在小屏幕的Activity布局中重复使用了它 :

    1
    2
    ArticleFragment frag = new ArticleFragment();
    getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();

    当然,这与在 XML 布局中声明片段的效果是一样的,但在这种情况下却没必要使用 XML 布局,因为报道片段是此活动中的唯一组件。

    请务必在设计片段时注意,不要针对具体活动创建强耦合。要做到这一点,通常可以定义一个接口,该接口概括了相关片段与其主活动交互所需的全部方式,然后让主活动实施该界面:

    例如,新闻阅读器应用的 HeadlinesFragment 会精确执行以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class HeadlinesFragment extends ListFragment {
        ...
        OnHeadlineSelectedListener mHeadlineSelectedListener = null;
     
        /* Must be implemented by host activity */
        public interface OnHeadlineSelectedListener {
            public void onHeadlineSelected(int index);
        }
        ...
     
        public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) {
            mHeadlineSelectedListener = listener;
        }
    }

    然后,如果用户选择某个标题,相关片段就会通知由主活动指定的侦听器(而不是通知某个硬编码的具体活动):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class HeadlinesFragment extends ListFragment {
        ...
        @Override
        public void onItemClick(AdapterView parent,
                                View view, int position, long id) {
            if (null != mHeadlineSelectedListener) {
                mHeadlineSelectedListener.onHeadlineSelected(position);