欢迎进入UG环球官网(环球UG)!

usdt承兑商合作(www.caibao.it):一个有关 V8 破绽的细节剖析 (二)

admin3周前24

USDT第三方支付

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

TurboFan

请注重,TurboFan是V8的一个极其庞大的组件,因此本节不会对其举行详尽的先容。

V8与当今大多数大型JavaScript引擎一样,最初只是JavaScript的编译器。随着盘算机的函数越来越壮大,人们希望最先在浏览器中运行越来越庞大的应用程序,由于面临现实,基于浏览器的应用程序才更具可移植性。只管仅使用了编译器,V8很快就到达了性能极限。

当到达限制时,V8开发职员就构建了TurboFan。下图大致演示了V8当前的事情方式:

V8执行历程

优化性

TurboFan会逐个对函数编译JavaScript代码,在JavaScript中最初几回挪用函数时,该函数由编译器执行,该编译器网络类型反馈。在这种情形下,类型反馈仅示意有关在函数中通报或使用的类型的信息。这种类型的反馈随后被TurboFan用于“推测性”优化,这基本上意味着TurboFan会使用这种类型的反馈做出假设来天生内陆代码。

让我们看一个例子,假设你有以下代码:

当add(5,10)在循环中被挪用10000次时,编译器将网络类型反馈,TurboFan将使用该反馈举行假设并天生本机汇编代码,将val1和val2像Smis一样简朴地相加。

这个本机汇编代码运行得异常快,然则这里有一个问题。 JavaScript是一种动态类型的语言,因此没有什么可以阻止你将字符串或工具通报给add函数。TurboFan需要提防这种情形,由于将两个字符串加在一起与将两个数字加在一起是完全差其余,以是在这两种情形下运行相同的本机代码会导致破绽。为防止这种情形,将类型防护插入到本机代码中,以检查val1和val2是否都是Smi类型,只有在类型检查通过时才执行本机代码。若是任何一项检查失败,那么代码将被优化,而且执行将释放给可以处置所有情形的编译器。

让我们使用V8的--trace-opt和--trace-deopt标志运行以上代码,以查看优化和反优化的历程:

你会看到,由于“挪用的类型反馈不足”,该函数获得了优化,厥后又作废了优化,这是有原理的!我们从来没有用字符串挪用过函数,因此没有类型反馈可以引用,因此是非优化的。

优化阶段

TurboFan不仅是一种推测性的编译器,它也是一个优化的编译器,这意味着它具有许多差其余优化阶段。

TurboFan的简略优化阶段

若是你以前学习过编译,则其中的某些术语对你来说似乎很熟悉。它们只是TurboFan在某些优化阶段执行的编译器优化。然则,这些优化对于剖析器天生的AST很难做到,因此TurboFan将AST转换为另一种示意形式:“sea of nodes”图。

sea of nodes

TurboFan在源代码的节点示意形式上举行了优化,我将简要先容它的事情原理,然则若是你愿意,可以同时两篇(1 ,2 )参考文章,以更好地明晰它。

sea of nodes图中的节点可以示意高级操作或低级操作,节点自己与边缘毗邻,而且有三种类型的边缘。

Value 边缘用于将值通报到可以使用它们的节点中, 在下面的示例中,值1和5使用Value 边缘通报给添加节点。

控制边缘用于界说控制流。例如:

最后,效果边缘(Effect 边缘s)用于显示更改程序状态的操作顺序。这些边缘由虚线界说(其他两种类型的边缘用实线绘制)。领会效果边缘的一种方式是思量一种方案,在该方案中,加载工具的属性,对其举行修改,然后将其存储回该工具中。在sea of nodes图中,加载节点的效果输出边缘将进入修改节点,而修改节点的效果输出边缘将进入存储节点。这样,引擎就知道操作的顺序是加载->修改->存储。

以下展示了效果边缘:

Turbolizer效果边缘

让我们实验查看我之前显示的谁人添加函数的节点示意形式,为此,我们将使用Turbolizer,这是由V8开发职员确立的工具,用于在各个优化阶段查看TurboFan的sea of nodes图。

在v8根目录下执行以下操作来设置Turbolizer(确保先安装最新版本的npm和nodejs):

完成此操作后,在Chrome或Chromium(必须是这些浏览器之一)中接见https://localhost:1337,你将看到以下截图:

Turbolizer截图

Turbolizer的事情方式是输入.json文件,你可以通过使用--trace-turbo选项运行V8天生该文件。例如,你可以运行add函数的代码,如下所示:

你看到的turbo-*.json是你需要上传到Turbolizer的文件。点击Turbolizer右上角的按钮并上传文件,你应该看到以下内容:

Turbolizer添加函数:图形构建器阶段

你可以在左侧看到现实的源代码,而在右侧你可以看到天生的装备代码。在中央,你会看到大量的节点图,当前图中隐藏了许多节点(这是默认行为)。

我们要查看Typer阶段中的所有节点,由于我们想看看TurboFan若何输入函数。从顶部的下拉菜单中,选择V8.TFTyper选项。接下来,点击“显示所有节点”按钮,然后点击“结构图”按钮,最后点击“切换类型”按钮。这将显示每个节点,整齐地显示图形,并显示TurboFan推测的类型。

该图当前在单个屏幕截图中无法查看的节点过多,因此我将有选择地隐藏不主要的节点。若是你想知道我若何知道哪些节点很主要而哪些节点不主要,那仅仅是基于以前使用过Turbolizer的知识。我建议只是实验使用差其余测试代码,然后看看Turbolizer若何部署sea of nodes,以领会更多信息,我之条件到的博客文章也先容了一些示例。

,

usdt收款平台

菜宝钱包(www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,

Turbolizer添加函数:Typer阶段

你会注重到一些事情:

1.一最先,我们有一个循环节点,它告诉我们,我们要进入一个循环。由此发生的进入分支和LoopExit节点的边缘是控制边缘。

2.一个带有Any类型的Phi节点和一个带有NumberConstant[10000]类型的局限(10000,10000)的节点被通报给一个带有Boolean类型的speculativenumberless节点。Phi节点是我们的循环变量,此处将其与NumberConstant [10000]举行对照以查看其是否小于10000。此处的边缘是Value 边缘。

3.SpeculativeNumberLessThan节点通向“分支”节点,该分支节点又会指导到一个IfTrue或IfFalse节点。若是到达IfFalse节点(即循环变量不小于10000),则只需执行LoopExit。

4.在IfTrue分支中时,会发生两件事。首先,我们使用SpeculativeSafeIntegerAdd将两个常量5和10一起添加,该常量已使用Range(15,15)准确输入。其次,我们看到循环变量添加了常量1。此添加的SpeculativeSafeIntegerAdd节点已使用Range(-9007199254740991,9007199254740991)举行了输入。这样做是由于循环变量的初始类型是Any,这意味着TurboFan需要处置所有可能将Any类型添加到常量1的情形,这是一个很大的局限。

5.Branch,IfTrue和IfFalse边缘之间的边缘都是控制边缘。

6.TurboFan输入使用Range(Min, Max)的数字,若是推测它们只是一个可能的值,那么最小值和最大值相等,否则它们是不相等的(例如在循环变量的情形下)。

7.TurboFan也为我们做了另一个优化,它已将add函数内联到循环中(请注重,没有JSCall指令,这是sea of nodes图函数挪用方式)。

我省去了返回效果值的退出案例,然则若是你好奇的话,可以在自己的Turbolizer图表上查看。图中也没有显示效果边缘,然则那是由于这里没有任何操作可以真正改变程序中任何变量或工具的状态。

本节的主要内容如下:

1.领会若何使用Turbolizer查看sea of nodes graph以获取优化的代码。

2.领会节点之间差异类型的边缘。

3.领会若何input node,这对于下一节中对第二个观点验证的注释尤为主要。

4.大致领会控制流在TurboFan中的事情方式。请注重,当我说控制流时,我并纷歧定只是指分支节点,还包罗若何确定节点执行的顺序。使用“结构图”选项将使Turbolizer大致对图举行排序,以便你可以使用自上而下的方式举行阅读。它通过跟踪每个节点的效果输出边缘来获得排序,但这并不总是100%准确的,因此你必须随时举行查找。固然,我会凭证需要在这篇文章中注释所有内容,因此你不必郁闷尚未完全领会此内容。

第二个POC(第二部门)

综上所述,我们现在就可以最终完成第二个观点验证的其余部门。

对Typer的处置

第二个观点证实的其余部门如下所示:

界说length_of_double的方式只是将现实浮点值存储到V8中的变量中的一种方式。请记着,JavaScript仅具有数字和BigInteger的观点。 Sergey在这里所做的是他将BigInteger值0x2424242400000000存储到BigUint64Array中,然后使用与BigUint64Array相同的缓冲区(后备存储)确立新的Float64Array。然后,他仅通过float数组接见0x2424242400000000值,并将其存储在length_as_double中,稍后你将看到若何使用它。

让我们从trigger函数的第一部门最先,看看它是若何事情的。我们将从sea of nodes graph最先,稀奇是在Typer阶段。执行./d8 --trace-turbo poc.js,在Turbolizer中打开新天生的turbo-trigger-0.json文件,然后切换到Typer阶段。首先,我将仅显示为该函数确立的节点,直到到达标有[1]的行:

TurbolizerTrigger函数Typer第1阶段(在移动装备上,在新标签页中打开图像)

仔细阅读图像,以查看Typer若何在第二次Math.max挪用之前输入所有节点。你会注重到,Typer可以准确执行所有操作,然则这里仍然存在一个尚未解决的大问题。

typer假定传入的数组的长度字段具有Range(0,67108862)的类型,这在图形顶部的LoadField [+12]节点中显示。它是从以下代码中获取的:

这里的问题是我们传入的数组(giant_array)的长度现实上为67108863!若是你切记此图,将会获得完全差其余图片。下面是观点证实的注释版本,它通过显示typer的想法与类型现实上是什么来演示该问题:

如你所见,最后的Typer确定x只能在range(0,1)的局限内,然则基于我们现实传入的数组,我们知道该局限现实上应该是range(0,7),由于x = 7!因此,我们实质上在这里诱骗了Typer。现在的问题是,我们若何行使这一点?

FOOLED TYPER == OUT OF BOUNDS WRITE

Trigger函数的下一部门如下:

让我们看一下该函数这一部门的turbolizer图:

Turbolizer Trigger Function Typer Phase 2(在移动装备上,在新选项卡中打开图像)

这张图有点庞大难读,由于它的逻辑有点杂乱,但希望它能让人人明晰要点。要害要点如下:

1.其中的CheckBounds节点很有趣,由于它会举行界线检查。在这种情形下,Range(0,1)只是从NumberMax节点的类型继续而来。 CheckBounds节点本质上接纳Input 边缘0的值,并确保其小于Input 边缘1的值。在本文中,“Input 边缘”是指从其上方进入该节点的边缘。Input 边缘0将是最左边缘的边缘,Input 边缘1将是第二个最左边缘的边缘,依此类推。

2.在这种情形下,Input 边缘0是第二个x = Math.max(x,1)挪用,该挪用返回值7(请记着,此处的Typer不准确)。这里的CheckBounds节点确保x在Range(1024, FixedArray::kMaxLength+1024)的局限内。这样做的缘故原由是由于写入大于length + 1024的索引将需要引擎将corrupting_array的后备存储转换为字典,这将需要对该函数举行优化。

3.关于CheckBounds节点要注重的另一件主要事情是,它们的函数与SpeculativeNumberLessThan节点略有差异。使用SpeculativeNumberLessThan节点,TurboFan会将推测的索引类型(Range(0,1))与限制类型举行对照。然则,对于CheckBounds节点,TurboFan到达该节点后,它将立刻获取索引节点的现实值(在本例中为x)并将其与限制类型举行对照。这意味着我们导致Typer将x错误地输入为Range(0,1)的事实没有任何意义。因此我们仍然需要确保其现实值小于或即是length + 1024,否则该函数将失去优化。只管在观点证实代码中x即是7,这对我们来说很好,然则要记着这一点很主要,由于我们可以让x即是险些任何值,而且仍然可以使用原始破绽将其输入为局限(0,1)。

4.通过CheckBounds节点后,将到达MaybeGrowFastElements节点。这个节点本质上是在检查是否需要在接见索引x之前增添corrupting_array。一旦通过该节点,我们最终有了一个StoreElement节点,该节点将length_as_double(这是0x2424242400000000的浮点示意形式)存储到corrupting_array [x]。

不外尚有一个问题需要搞明晰,此MaybeGrowFastElements节点是什么,它若何事情?我们不希望它在接见索引x之前就增添corrupting_array,由于这样它就不会再越界接见了。

为了查看MaybeGrowFastElements节点的事情方式,我们需要看一下TurboFan的载荷祛除优化阶段。在此阶段,若是TurboFan遇到MaybeGrowFastElements节点,则最终会通过挪用TypedOptimization :: ReduceMaybeGrowFastElements函数来对其举行优化:

查看之前的Turbolizer图,你会注重到,value input 边缘 2是早年面的CheckBounds节点进入的solid 边缘。此边缘将包罗typer凭证最终Math.max(x,1)挪用确定的局限,即Range(0,1)。它存储在[1]的index变量中。

另一方面,value input 边缘 3是LoadField节点,该节点在[2]处加载corrupting_array后备存储区的长度。在图中将其输入为Range(0,134217725),这与Range(0,FixedArray :: kMaxLength)相同,但厥后通过LoadElimination :: ReduceLoadField被优化为具有Range(2,2)类型的NumberConstant。Typer可以执行此操作,由于它知道一个事实,即corrupting_array的长度为2。

经由优化的LoadField节点最终会是适合我们的操作,由于一旦到达[3]处的if语句,index_type.Max()返回1(Range(0,1)),而length_type.Min()返回2 (Range(2,2)),这意味着index_type.Max()<length_type.Min()返回true,然后执行if语句。

执行进入if语句的事实会告诉TurboFan以下内容:我们险些可以一定,正在接见的数组(在这种情形下为corrupting_array)将永远不需要凭证编写代码的方式来增进。若是索引可以拥有的最大值始终小于长度可以拥有的最小值,为什么我们还要增添数组呢?

本文翻译自:https://www.elttam.com/blog/simple-bugs-with-complex-exploits/,content:
上一篇 下一篇

猜你喜欢

网友评论

  • 2021-03-27 00:00:57

    Allbet Gaming欢迎进入Allbet Gaming网址:www.allbetgame.us。Allbet Gaming网址开放Allbet Gaming会员登录网址、Allbet Gaming代理后台网址、Allbet Gaming注册、Allbet Gaming代理开户、Allbet GamingAPP下载、Allbet Gaming电脑客户端下载等业务。都可以当范本了

随机文章
热门文章
热评文章
热门标签