应用签名

数字签名

数字签名(又称公钥数字签名、电子签章等)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。
数字签名,就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。

数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用Hash函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。

举个例子:移动客户端向服务器发送数据

客户端发送数据

如果不做任何处理,可能会遭到中间人的窃取攻击,后果如何严重就不说了。那么我们如何防止中间人拦截,或者检查数据是否被篡改呢?
直接用RSA(RSA加密原理)进行加密应该是不满足我们的需求,RSA只适合对小数据进行加密,我们知道验证数据的完整性可以用Hash(Hash概述)来验证,可以对数据进行Hash,把Hash值和原始数据一起打包发送给服务器,服务器将原始数据进行Hash,得到的hash值和客户端发送的Hash值做对比,如果一致则保证数据有效性。但是这样会有安全隐患,如果中间人篡改了客户端发送的数据,当然也可以修改客户端发送的Hash值,所以这样操作不可行。

这时我们可以用RSA来对hash值进行保护,此时客户端发送原始数据,和经RSA加密后的该数据的hash值。

数字签名

服务器对RSA加密的数据进行解密,得到原始数据的hash值,接下来对原始数据进行通过同样的Hash算法,将得到的Hash值和解密后的Hash值做对比,如果一致则保证数据有效性,整个过程中,如果解密的Hash值和原始的Hash值不一致,或者无法解密RSA的数据,说明数据被篡改了。

3.jpg

现在我们解决了服务端收到客户端发送数据的有效性,此时我们称,对原始数据Hash值进行RSA加密后的数据,是原始数据的数字签名。简单解释数字签名也就是对原始数据的Hash值进行非对称加密。

代码签名

代码签名是对可执行文件或脚本进行数字签名,用来标识软件来源以及软件开发者的真实身份,确认软件在签名后未被修改或损坏的措施。和数字签名原来一样,只不过签名的数据是代码而已。

苹果也是通过代码签名来保证每一个安装到iOS上的APP都是经过苹果官方允许的,防止盗版软件、病毒入侵、静默安装等。如果想要实现验证,最简单的方式就是通过苹果官方生成非对称加密的一对公私钥,在iOS系统中内置一个与服务器对应的公钥,私钥由苹果后台来保存,我们传APP到App Store时,苹果后来用私钥对APP数据进行签名,iOS系统下载这个APP后,用公钥验证这个签名,如果签名正确则这个APP肯定是由苹果后台认证的,并且没有被修改或损坏。

代码签名

整个过程很简单,这样就保证了苹果安装的每一个APP都是经过苹果官方允许的。对于大部分普通用户而言,这样一个数字签名就解决了安全隐患问题,但是实际上iOS设备安装APP并不是只有App Store这一个渠道,比如对于我们iOSer来说,我们在开发APP时还在真机调试,当然苹果还开放了企业内部分发的渠道,这时就无法通过简单的代码签名来满足这些需求了。

苹果为了实现这些需求,iOS签名的复杂度也就开始增加了,这样双层代码签名就出现了。前提,我们都知道描述文件,但是描述文件具体是干嘛的呢

描述文件

在真机调试时候,都会有一个描述文件,描述文件就是在developer.apple.com创建的,在Xcode中填入AppID后会代办创建,Xcode运行时会打包进APP中。为了系统安全,苹果除了控制APP滥用问题还控制了推送、iCloud、调试器等附加这些权限,苹果把这些权限开关统一称为Entitlements(授权文件)。并将这个文件放在了一个叫做Provisioning Profile(描述文件)文件中,描述文件里面就包括权限、证书等配置相关文件。

通常,描述文件会保存在~/Library/MobileDevice/Provisioning Profiles/这个文件中,可以在终端用 security cms -D -i + [名称]命令查看描述文件里面的信息,我们会发现,描述文件是一个plist文件,下面是个人描述文件信息演示,并对一些信息做出了注释,部分关键内容被隐藏,Base64(了解Base64编码解码)内容被删减

$ security cms -D -i 294b2de0-a877-4f33-9825-9a8***.mobileprovision
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict><key>AppIDName</key><string>com *** </string><key>ApplicationIdentifierPrefix</key><array><string>***GCDB</string></array><key>CreationDate</key><date>2019-03-06T08:54:06Z</date><key>Platform</key><array><string>iOS</string></array><key>IsXcodeManaged</key><true/><key>DeveloperCertificates</key><array><data>***vcNAQELBQAwgZYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgwNzEwMDIzNsb3BtZW50IENvLixMdGQuMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO4xNm+/SXHwULul2Am4b+2/a919AfaDSy6Jw+nC3599RNhUlY+/PNTudcsBUsSw53+flAh6dVVGO77lebM1GaveXMLr65l9aZe2a6ewR0QOpSkvoBZBXlRA14WpyfdMbU7VlWutKiFHsuxA4KSQyoagY8GJ3tB5vSlxRtfix0TKtOCMx9v1iYdCztmhmtt5J6GZn8jKszkPgKxMNvm4MD9N/pr7/Z0gX06oywYb3DpS7uQKdffsLZyj05H0HvSg6V4nHZw5HNIf8qd1VjOiI7NGcvsOwltfGmiOhmxQjaESwalgX7vWg7ij8fh9ke1on8veQgptIxutKjLWG9JnJ2LPOeP7w3PJC03Yl89qJ6F0VAyA1+ck1ieimrG0yXkd9z/YWMd/puDoW7SmEQ/WdKyQkyld0iSnkQ90511uOAp0/yDgaaouyUDZlcIjL2/4JzdEJEiRkZEdmw63uYe4dwXSyTyLlA+ntka2QlKEiJmy8oyPTqjEuqcWWQmnFYWiEBqPaeEXlXT9uuGjqn9aN3MaTgD8QFIyOhonc6ReQHtx8apXGEzhVNAQXCLuKSB04JiCbL3YE2XT42QlygBL+7ROxC00pAIltrPkFfuoyBbpnj9pWQ==</data><data>***GVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgwNTI1MTMxMjA4WhcNMTkwNTI1MTMxMjA4WjCBrjEaMBgGCgmSJomT8ixkAQEMCjVFVE45NEpBNlUxMTAvBgNVBAMMKGlQaG9uZSBEZXZlbG9wZXI6IFhpbiBDaGVuZyAoNEU4Mk5RVzZDMikxEzARBgNVBAsMClpFV0pNSEdDREIxOzA5BgNVBAoMMlNoYW5naGFpIFJpc2VuIEVsZWN0cmljIFBvd2VyIERldmVsb3BtZW50IENvMDYGCCsGAQUFBwIBFipodHRwOi8dEU39NyqZsQBCA/P1txkkx9sI7JsMcMnjwa/N5QCg+gJLvMEh1ZvQ/rroTtyvDxFuuMrQkiZeeBGs8qBO2Jre6ma32mMo5kSjc9w9AtnFwlQHrW3+HPwwqlThRrMeNvbLZAvZhoENb04HE26sH4k1tk8CrNvutsjl+K3GGnuvWCnZy/dT57wrGAMrlkQZRWXomSxr9y+F4ArsMj+4UsA==</data><data>***sZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgwNTEzMDQ1OTQ0WhcNMTkwNTEzMDQ1OTQ0WjCBrjEaMBgGCgmSJomT8ixkAQEMCjVFVE45NEpBNlUxMTAvBgNVBAMMKGlQaG9uZSBEZXZlbG9wZXI6IFhpbiBDaGVuZyAoNEU4Mk5RVzZDMikxEzARBgNVBAsMClpFV0pNSEdDREIxOzA5BgNVBAoMMlNoYW5naGFpIFJpc2VuIEVsZWN0cmljIFBByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwMwHQYzizUHwwrgDSFxwfGfc71fr4hz3PmN6o7XqtGcNO4Af49JdDMZv6VQ5mr/plPBSDdun0D5ZfNDr9cXopDCzy+XPZB/H1ARmy1MT4vuKvdsGuUKBvJX/HinDi38G6AuwZ0alfPY9xqJ7WB7K14kaVdaaqOHMThOA7b7b1Stu+MjAKYJ6aUkrf9vvDbDAkQFi8lfh4L3Vya9MoXEpDpCNxQrUJhc6sy2eMf1NT5Q==</data><data>***zA5BgNVBAoMMlNoYW5naGFpIFJpc2VuIEVsZWN0cmljIFBvd2VyIERldmVsb3BtZW50IENvLixMdGQuMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKIWs9nQdTq3yqKLYVmjrGrgh6MhaWlkZb5MeHWKwGfky3/n/FaXmrLNxFFFWeFihdg4zmFjSjRl8ccvPF3afdZU2qW4Co1aKu459CnPhknFNbFtcrs0V1T6u6p3RJn6togsWn1z33IakruJPYtwY7k4S5jb20vb2NzcDAzLXd3ZHIwMTCCAR0GA1UdIASCARQwggEQMIIBDAYJKoZIhvdjZAUBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGVAFqGwKrYTc+8/GyszOm3u4/mn5s5b58ORnITE+A9bNtDGv6Qz46ev3ZMCrQZFwye//Tk+BI7ms4++jkj/pcmUCULtZw178cTRBpMT4P7tzgH5mRP/BpcSd/rv8994UXYPYdpXa4epFhchlCvu8dT8sQ38fFeahyd85nS30c4RxyW32bzKnzzRSedr/j4Y1qIIvzKdWPcnwkA5RYH8naxYfzEXL2321tdwfR5skvApeP/S2oX9WC+18XBk8Yy693rJoqrKdwkOzQSVGcA==</data><data>***3NjZOMlBGS00xMzAxBgNVBAMMKmlQaG9uZSBEZXZlbG9wZXI6IEhhb3lhbmcgWmh1IChLTU5VVTI4QlBaKTETMBEGA1UECwwKWkVXSk1IR0NEQjE7MDkGA1UECgwyU2hhbmdoYWkgUmlzZW4gRWxlY3RyaWMgUG93ZXIgRGV2ZWxvcG1lbnQgQ28uLEx0ZC4xCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA19bNzMNJ0uSYvwNygYH8b4GQY11UmNvbS9vY3NwMDMtd3dkcjAxMIIBHQYDVR0gBIIBFDCCARAwggEMBgkqhkiG92NkBQEwgf4wgcMGCY2QGAQIBAf8EAgUAMA0GCSqGSIb3DQEBCwUAA4IBAQCJw3MoEbhJNVyc1PFG6FtY7QZAy+gmqRz9YiwnFh3y5Qj5lnC2n0WdG8SrlZA+TKQgfwRZNEB6ZIfDZOJAFsqpBMAauC1V0CDzP8UWBDfluoVCU89Ns5juag8ffk/ulEQfEN1NakgiSwihy+QEmd2PWBTn4dfGVSV9mYRjPppFdM6kik1WLgMDZfLRmNzZ0MzMZMAPy8gdMmQiM7uBY1v+EnUXfyjKnK7Y28AB9c6oQHPoTGmVQxZjJgkejyOlBPWwVPbNvvzNRPuBaHI8muX2HzjsA9SJowEBXKICinqLSFZ8NDQVBrw2fgDs5MD5vaDHXtrz9t6Ahw6/7nUB240U</data><data>***LDCNBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgwOTI1MDIxMzA4WhcNMTkwOTI1MDIxMzA4WjCBrzEaMBgGCgmSJomT8ixkAQEMCjIzVDNTWlo0WjQxMjAwBgNVBAMMKWlQaG9uZSBEZXZlbG9wZXI6IOS9s+a7qCDnjosgKDN1UdDgQWBBT7im/bw3RlTv0soM/RVB2mmh9e4zAOBgNVHQ8BAf8EBAMCB4AwEwYKKoZIhvdjZAYBAgEB/wQCBQAwDQYJKoZIhvcNAQELBQADggEBACErmsYfmt/qn5yj86poEkDsDWhznabHVn1CJprtzyHbXNcbKJE1pY99ACzSUVW9iGaxXJ9m5dWmZsd+2gxOBthFDq4mSpU50OHQgg9AjjugvsQ+OH1r1qgqcJMPdLN4stJtkAC9Mb/rt3AYA5eEdHOXbhrsE3TXWmWfJzYS9PFyarGcJ9xFAWg76jTtFA6nlzYtl/YyMDS6Z075oMzKG/pRfCI13P4AqRKdx/DBtz4x0Wv59LVMVrXa4bfePLKMBJrBFdiiyCy4k4BIx4weG/Y/8OsEkmWkN0fDWmi4MZjy3I+FCNkv9tUzI/vRvDEiNR7qo5rGRKnAi+X1QbdLYcU=</data><data>***o0WjQxMjAwBgNVBAMMKWlQaG9uZSBEZXZlbG9wZXI6IOS9s+a7qCDnjosgKDNYTDJNRFdWOUcpMRMwEQYDVQQLDApaRVdKTUhHQ0RCMTswOQYDVQQKDDJTaGFuZ2hhaSBSaXNlbiBFbGVjdHJpYyBQb3dlciBEZXZlbG9wbWVudCBDby4sTHRkLjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC35nKt/AoSiANJn9e9H96rol/85zSEAjAAMB8GA1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MD8GCCsGAQUFBwEBBDMwMTAvBggr1UdJQEB/wQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBSIeBiIaX4EyW39H2laObRLnSyWVjAOBgNVHQ8BAf8EBAMCB4AwEwYKKoZIhvdjZAYBAgEB/wQCyETSZ/FOr8EBZBVLmMGXz03x6f2Hpd7QsmoJJM5+6hHu4qgstgXNg0RRsa0B4jScKTMlRxmdZuLjm3plaX+P+yo0ylAnvGWm1sx9mxPTgrDbUg7Rg0n1bhrBtkX47+r8SUz4+E6dHnqZ7x48hZlhv6SFkj4PQ/apeAvITvnLeq7bj586gkpwS30bjmrsqSO8aEnysvyxq6Xx3+seH9Uihmjb7XdnV25mKfbf5ms6sm+HrN6ifrDb0LePX8YAsSH4=</data></array><key>Entitlements</key>      // 权限<dict><key>keychain-access-groups</key><array><string> *** .*</string>     </array><key>get-task-allow</key>    // APP是否允许调试<true/><key>application-identifier</key>    // appID<string> *** </string><key>com.apple.developer.associated-domains</key><string>*</string><key>com.apple.developer.team-identifier</key><string> *** </string><key>aps-environment</key><string>development</string></dict><key>ExpirationDate</key>    // 过期时间<date>2020-03-05T08:54:06Z</date><key>Name</key><string>iOS Team Provisioning Profile: *** </string><key>ProvisionedDevices</key>    // 设备列表<array><string>476c21e91700a0605a11a***3de7705a</string><string>98a1263783fb5538ec292f0***9d05690ffb</string><string>bc9144d7496e5337e1f***cb653dc42e</string><string>254fbcad0126f989b8980***d1dd8169b6ca</string><string>349f7aeb300473cc30c28***025d</string></array><key>TeamIdentifier</key><array><string> *** </string></array><key>TeamName</key><string> *** Co.,Ltd.</string><key>TimeToLive</key><integer>365</integer><key>UUID</key>    // 描述文件的UUID<string> *** </string><key>Version</key><integer>1</integer>
</dict>

双层代码签名

  • 请求证书
    开发过程中,首先Mac电脑(比如Xcode)会自动生成一对公私钥

公私钥

图中的证书就是公钥M,专用密钥就是私钥M(也就是我们导出的P12文件)

用一个CSR文件(就是在钥匙串访问中,证书助理,从证书颁发机构中获取的)向苹果申请一个证书,这个CSR文件主要包含了一个公钥文件,还有一些信息,比如邮箱、名字、签名信息、Hash值等等,苹果收到请求后,会用私钥A将公钥M进行签名,以供苹果设备进行验证(用公钥A进行验证)。苹果服务器将公钥M和签名信息打包成证书,并把appID、证书、设备IDs、权限文件等放入描述文件一并返回给Mac电脑,以备Mac电脑用这个描述文件中的证书到iOS设备去验证。

请求证书

  • 生成IPA文件,发送到iOS设备上
    iOS设备上的APP其实都是文件夹,最重要的是可执行文件MachO和framework,APP签名也就是对它俩进行签名,通过Mac电脑的私钥M对文件进行签名,把这个签名和从苹果申请的描述文件一并放入到APP中。当安装APP时,iOS设备用公钥A来解析描述文件中的证书进行验证证书的有效性,通过之后将证书中的公钥M拿出来,再去验证APP签名的有效性,这样就可以验证当前APP是否是苹果官方允许的。

双层代码签名

在开发阶段,我们需要频繁的改动程序跑真机,苹果不需要关心这些,所以iOS设备没有对APP的更改进行验证,只是验证证书(公钥M是不是合法的,APP签名的有效性)。这样解决了安装问题,但是如果这样的话,可以在任何一部iOS设备上安装APP,苹果防止开发者滥用,又加了两个限制:

  1. 要在苹果后台注册过的设备才可以安装
  2. 签名只能针对具体的某一个APP进行签名

关于APP签名

在开发中,编译一个APP后,用本地的私钥M对APP进行签名,同时把从苹果服务器得到的Provisioning Profile文件打包进APP中,文件名为embedded.mobileprovision,把APP安装到iOS设备后,系统进行验证。

查看资源文件签名

查看APP的包内容,里面会有_CodeSignature文件夹(里面的就是资源文件的签名),还有个可执行文件,可以用MachO查看,里面的Code signature就是应用签名。

该文章为记录本人的学习路程,希望能够帮助大家,也欢迎大家点赞留言交流!!!文章地址:https://www.jianshu.com/p/4307de384a50

iOS应用签名原理--数字签名?代码签名?双层代码签名?相关推荐

  1. iOS 应用签名原理

    欢迎访问我的博客原文 不少果粉对 Apple 钟情,与它的纯净.安全有很大关系,我们发现在苹果的设备上下载应用时,不会出现触发下载一系列垃圾软件的情况,而且用户可以明确 App 的来源--通过官方商店 ...

  2. ios safari 描述文件 跳转到_iOS 超级签名之描述文件的实现过程

    简介 因为最近企业签掉得太严重了,上头要求实现超级签进行游戏下载.故有了此文章,记录一下过程. 签名原理其实很简单,超级签名的技术就是使用个人开发者账号,将用户的设备当作开发设备进行应用分发.这也导致 ...

  3. iOS逆向一:数字签名苹果应用双重签名原理应用重签名

    hash hash(哈希)算法是指将任意长度的文本,通过一个算法后得到一个固定长度的文本(也可能是二进制数据),哈希其实是一种思想,所有符合这种思想的算法都可以称之为哈希算法(不如MD5,sha1,s ...

  4. iOS应⽤签名原理浅析

    目录 1. 前文 2. 数字签名 3. 简单代码签名 4. 双层代码签名 5. 描述文件 6. 结束语 1. 前文 还记得刚开始开发iOS APP的时候,总是在真机调试这块弄的云里雾里的,什么证书,什 ...

  5. ios重签名shell脚本_03:双层签名和重签名流程以及shell脚本重签名

    复习 1:双向签名7不流程 2:查看签名信息和证书信息 3:查看用户权限 4:shell脚本签名 目录 1:双向签名7不流程 2:查看签名信息和证书信息 3:查看用户权限 4:shell脚本签名 正文 ...

  6. S60 手机操作系统数字签名安全机制与软件签名原理

    <计算机世界>第 20 期文章 < S60 V3 手机操作系统用证书折磨你>是从一个普通手机用户的角度来写的,反映了一些用户的心声,但从手机软件专业和手机产业来讲,此文章有失公 ...

  7. iOS证书签名原理分析

    在iOS真机调试和发布上线的时候,我们可能已经习惯了配置各种证书.描述文件,等这一繁琐的步骤.但是对于背后我们为什么要配置这些东西,以及其背后的原理之前一直没有做过分析研究,最近有空就简单的研究了一下 ...

  8. ios重签名shell脚本_iOS逆向——shell重签名及代码注入

    感谢hank老师 上一章我们讲了应用签名原理和重签名原理,不再赘述,没有看过的同学可以点这里-- 这章概述shell脚本重签名.可能有的同学认为有很多工具都能快速的帮助我们重签名实现需求,但我更希望在 ...

  9. iOS-APP 签名原理

    iOS 签名机制挺复杂,各种证书,Provisioning Profile,entitlements,CertificateSigningRequest,p12,AppID,概念一堆,也很容易出错,本 ...

最新文章

  1. SQL Server DBA工作内容详解
  2. Zabbix 监控获取apache_status
  3. 笔记-项目人力资源管理-建设项目团队
  4. 对于初学者学习Java语言的建议(转载)
  5. 基于Flask实现后台权限管理系统 - 导言
  6. C/C++插入数据到Mysql数据库中
  7. 扫帚:我天天都能立起来,看把你们闲的
  8. matlab createtask,Matlab批量与createjob
  9. stm32实验报告心得体会_嵌入式第9次实验报告
  10. Python 3.9 正式版要来了,会有哪些新特性?
  11. video.h5.player.js视频播放器
  12. 谷歌浏览器一直无法上网,其他浏览器可以正常上网
  13. 技术成长-不积跬步无以至千里
  14. 区块链51%双花攻击
  15. 华为云计算工程师证好考吗?
  16. 【kali技巧】查看宿主机windows的ip地址
  17. Docker学习01---狂神说
  18. WebP支持:超乎你想象
  19. 忘记ios访问限制密码
  20. 怎么用linux查看xml文件格式,xml是什么格式?xml文件格式用什么软件可以打开

热门文章

  1. mysql8.0.17压缩包安装教程_超详细的MySQL8.0.17版本安装教程
  2. 初学者python书籍推荐_2019年Python入门书籍推荐
  3. 六、爬虫中重要的解析库xpath和BeautifulSoup
  4. html不读取缓存,如何让前端浏览器不进行缓存
  5. xp装linux一键安装教程图解,XP下硬盘安装CentOS 6.0图解教程
  6. java线程名_java多线程
  7. 机器学习著名定理之—No Free Lunch定理详解
  8. 浅谈个性化推荐系统中的非采样学习
  9. ECCV 2018 | Pixel2Mesh:从单帧RGB图像生成三维网格模型
  10. 路痴的单身小菡 BFS求最短路径+DFS求路径数