[聚合文章] Javascript DataView

JavaScript 2018-01-21 22 阅读

DataView 是一个可以从 ArrayBuffer 对象中读写多种数值类型的底层接口,在读写时不用考虑平台字节序问题。

示例代码:

<html>
<body>
<script>
var array_buffer = new ArrayBuffer(0x10);
var data_view = new DataView(array_buffer, 0, array_buffer.byteLength);
</script>
</body>
</html>
 

测试平台是 win10 x64 Edge 在windbg里搜索 DataView :

0:025> x chakra!Js::*DataView
00007ff8`37235938 chakra!Js::JavascriptLibrary::CreateDataView = <no type information>
00007ff8`37349190 chakra!Js::DataView::DataView = <no type information>
00007ff8`375c4318 chakra!Js::BuiltInPropertyRecords::DataView = <no type information>
 

DataView::DataView 就是 DataView 的构造函数,下断点调试示例代码。

0:025> g
Breakpoint 2 hit
chakra!Js::DataView::DataView:
00007ff8`37349190 488bc4          mov     rax,rsp
0:008> gu
chakra!Js::JavascriptLibrary::CreateDataView+0x4f:
00007ff8`37235987 4883c430        add     rsp,30h
0:008> dq da28f16180 l10
000000da`28f16180  00007ff8`374d48c0 000000da`28ed0100
000000da`28f16190  00000000`00000000 00000000`00000000
000000da`28f161a0  00000000`00000010 000000da`28f93640
000000da`28f161b0  00000000`00000000 000000da`331ebff0
000000da`28f161c0  00000000`00000000 00000000`00000000
000000da`28f161d0  00000000`00000000 00000000`00000000
000000da`28f161e0  00000000`00000000 00000000`00000000
000000da`28f161f0  00000000`00000000 00000000`00000000
0:008> ln 00007ff8`374d48c0
(00007ff8`374d48c0)   chakra!Js::DataView::`vftable'   |  (00007ff8`374d4be8)   chakra!Js::ProjectionArrayBuffer::`vftable'
Exact matches:
    chakra!Js::DataView::`vftable' = <no type information>
 

确实得到了一个 DataView 对象,偏移 0x20 处为 ArrayBuffer 的长度 0x10 ,偏移 0x28 处为 ArrayBuffer 对象的地址:

0:008> dq poi(da28f16180+0x28) l10
000000da`28f93640  00007ff8`3742d1e0 000000da`28ed2040
000000da`28f93650  00000000`00000000 00000000`00000000
000000da`28f93660  000000da`28e35f00 00000000`00000000
000000da`28f93670  000000da`331ebff0 00000000`00000010
000000da`28f93680  000000da`28f92981 00000000`00000000
000000da`28f93690  00000000`00000000 00000000`00000000
000000da`28f936a0  00000000`00000000 00000000`00000000
000000da`28f936b0  00000000`00000000 00000000`00000000
0:008> ln poi(poi(da28f16180+0x28))
(00007ff8`3742d1e0)   chakra!Js::JavascriptArrayBuffer::`vftable'   |  (00007ff8`3742d4f0)   chakra!Js::ArrayObject::`vftable'
Exact matches:
    chakra!Js::JavascriptArrayBuffer::`vftable' = <no type information>
 

现在修改代码,添加对 dataview 的操作:

<html>
<body>
<script>
var array_buffer = new ArrayBuffer(0x10);
var data_view = new DataView(array_buffer, 0, array_buffer.byteLength);
data_view.setInt32(0,12,true);
</script>
</body>
</html>
 

这次 dataview 如下:

0:011> dq 000000da`28f176c0 l10
000000da`28f176c0  00007ff8`374d48c0 000000da`29278900
000000da`28f176d0  00000000`00000000 00000000`00000000
000000da`28f176e0  00000000`00000010 000000da`28f93600
000000da`28f176f0  00000000`00000000 000000da`33523ff0
000000da`28f17700  000000da`29269a80 000000da`28f17840
000000da`28f17710  000000da`28f17780 000000da`28ef8200
000000da`28f17720  00000000`00000000 00000000`00000000
000000da`28f17730  00000000`00000000 00000000`00000000
 

ArrayBuffer :

0:011> dq 000000da`28f93600 l10
000000da`28f93600  00007ff8`3742d1e0 000000da`29278980
000000da`28f93610  00000000`00000000 00000000`00000000
000000da`28f93620  000000da`28e35080 00000000`00000000
000000da`28f93630  000000da`33523ff0 00000000`00000010
000000da`28f93640  00007ff8`374391d8 3d26f15c`00000619
000000da`28f93650  00000018`00000000 00610072`00720061
000000da`28f93660  00750062`005f0079 00720065`00660066
000000da`28f93670  00000000`00000000 00000000`00000000
 

ArrayBuffer 对象偏移 0x30 处其实为缓冲区地址,对应到 DataView 对象偏移 0x38 处,在用 setInt32 设置值后缓冲区内容:

0:011> db 000000da`33523ff0 l10
000000da`33523ff0  0c 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0:011> dq 000000da`33523ff0 l2
000000da`33523ff0  00000000`0000000c 00000000`00000000
 

可以看到,用 setInt32 以小端序模式填入值 0xc ,用 getInt32 可以读出来:

> data_view.getInt32(0).toString(16)
"c000000"
 

不加 true 为默认字节序,得到结果如下:

> data_view.setInt32(0,12);
undefined
> data_view.getInt32(0).toString(16)
"c"
> data_view.getInt32(1).toString(16)
"c00"
 

DataView 的成员函数还可以用如下方式调用,加 call 的话要带上 this ,也就是 data_view :

data_view.setUint32.call(data_view, 0, 12, true);
 

DataView 可以用来做地址读写,只要能够利用漏洞修改 DataView 对象的长度字段和缓冲区地址字段即可。

修改代码为:

<html>
<body>
<script>
var array_buffer = new ArrayBuffer(0x10);
var data_view = new DataView(array_buffer, 0, array_buffer.byteLength);
alert("change memory");
data_view.setInt32(0x12345678, 0xffffffff, true);
alert("Done");
</script>
</body>
</html>
 

DataView 对象:

0000004c`f7abbe40 00007ff8374d48c0 chakra!Js::DataView::`vftable'
0000004c`f7abbe48 0000004cf7ad4040 
0000004c`f7abbe50 0000000000000000 
0000004c`f7abbe58 0000000000000000 
0000004c`f7abbe60 0000000000000010 
0000004c`f7abbe68 0000004cf7b50ec0 
0000004c`f7abbe70 0000000000000000 
0000004c`f7abbe78 00000044f3f55b90 
0000004c`f7abbe80 0000004cf7b6e340 
0000004c`f7abbe88 0000004cf7abbfc0
 

ArrayBuffer 对象:

0000004c`f7b50ec0 00007ff83742d1e0 chakra!Js::JavascriptArrayBuffer::`vftable'
0000004c`f7b50ec8 0000004cf7ad40c0 
0000004c`f7b50ed0 0000000000000000 
0000004c`f7b50ed8 0000000000000000 
0000004c`f7b50ee0 0000004cf72f5360 
0000004c`f7b50ee8 0000000000000000 
0000004c`f7b50ef0 00000044f3f55b90 
0000004c`f7b50ef8 0000000000000010 
0000004c`f7b50f00 0000000000000001 
 

缓冲区:

00000044`f3f55b90 0000000000000000 0000000000000000
 

在弹窗后 break 下来修改长度字段和缓冲区地址:

0:022> dq 0000004c`f7abbe40 l1
0000004c`f7abbe40  00007ff8`374d48c0
0:022> dq 0000004c`f7abbe40+0x20 l1
0000004c`f7abbe60  00000000`00000010
0:022> ed 0000004c`f7abbe40+0x20 ffffffff
0:022> dq 0000004c`f7abbe40+0x20 l1
0000004c`f7abbe60  00000000`ffffffff
0:022> dq 0000004c`f7abbe40+0x38 l1
0000004c`f7abbe78  00000044`f3f55b90
0:022> ed 0000004c`f7abbe40+0x38 00000000
0:022> dq 0000004c`f7abbe40+0x38 l1
0000004c`f7abbe78  00000044`00000000
 

修改后的对象:

0000004c`f7abbe40 00007ff8374d48c0 chakra!Js::DataView::`vftable'
0000004c`f7abbe48 0000004cf7ad4040 
0000004c`f7abbe50 0000000000000000 
0000004c`f7abbe58 0000000000000000 
0000004c`f7abbe60 00000000ffffffff 
0000004c`f7abbe68 0000004cf7b50ec0 
0000004c`f7abbe70 0000000000000000 
0000004c`f7abbe78 0000004400000000 
0000004c`f7abbe80 0000004cf7b6e340 
0000004c`f7abbe88 0000004cf7abbfc0 
 

现在长度变成了 0x00000000ffffffff ,缓冲区地址 0x000000ef00000000 。现在可以读写的范围为 [0x000000ef00000000, 0x000000efffffffff]

如果 0x000000ef12345678 地址可写,那么就会写入 0xffffffff

注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。