js逆向学习笔记【一篇就够】

本文详细介绍了JavaScript的逆向工程,涵盖算法还原、基本数据类型、对象、函数、异常处理、DOM操作等多个方面,旨在提供全面的JS逆向学习资源。重点讲解了数据类型转换、对象属性操作、函数作用域、异步操作与Ajax请求,以及DOM和浏览器模型的交互。
摘要由CSDN通过智能技术生成

js逆向学习笔记【一篇就够】

  • 不够再来一篇

文章目录

算法还原

白盒还原

  • 直接扣算法,或者是标准算法
  • 理解对方 js 的意思,能够翻译成其他语言,可能会占用较长的分析时间

黑盒还原

  • 直接整体调用(加密复杂,更新频繁,整体关联度高)
  • 不需要关注算法逻辑,需要模拟浏览器环境,需要对抗环境检测

RPC 调用

  • 算法复杂度高,浏览器环境难以模拟
  • 找到算法位置,暴露出来,直接 rpc 调用,需要保证浏览器状态(内存泄漏,保活)

浏览器自动化

  • 无法逆向
  • 接近真人,但是有大量的自动化痕迹;

基本数据类型

  • 数值 Number:整数和小数
  • 字符串 String:文本
  • 布尔值 Boolean:布尔值,true 表示真,false 表示假
  • undefined:未定义,或者不存在
  • null:表示空值
  • 对象 Object:各种值组成的集合

原始类型

  1. 数值
  2. 字符串
  3. 布尔值
  • 原始类型就是最基本的数据类型,不能再进行细分;
  • undefined 和 null 一般看成是两个特殊值;

合成类型

  • 对象
    • 狭义的对象 Object
    • 数组 Array
    • 函数 Function
  • 一个对象往往是由多个类型的值组成,可以看成是一个存放各种值的容器

查看类型

  • typeof:返回一个值的数据类型

  • instanceof:表示对象是否是某个构造函数的实例

  • Object.prototype.toString

  • typeof 可以用来检查一个未声明的变量,而不报错;

    // 基本数据类型
    var tmp1 = "字符串";
    var tmp2 = 1;
    var tmp3 = 1.1;
    var tmp4 = true;
    
    // 特殊类型
    var tmp5=  undefined;
    var tmp6 = null; // null 是一个 object
    
    // 对象
    var tmp7 = {}; // 对象
    var tmp8 = []; // 数组
    var tmp9 = function(){}; // 函数
    
    console.log("typeof(tmp1)", typeof (tmp1));
    console.log("typeof(tmp2)", typeof (tmp2));
    console.log("typeof(tmp3)", typeof (tmp3));
    console.log("typeof(tmp4)", typeof (tmp4));
    console.log("typeof(tmp5)", typeof (tmp5));
    console.log("typeof(tmp6)", typeof (tmp6));
    console.log("typeof(tmp7)", typeof (tmp7));
    console.log("typeof(tmp8)", typeof (tmp8));
    console.log("typeof(tmp9)", typeof (tmp9));
    
    // function 因为有类object 的操作, 所以也属于 object
    console.log("tmp9.name", tmp9.name); // 获取函数名
    
    //typeof(tmp1) string
    //typeof(tmp2) number
    //typeof(tmp3) number
    //typeof(tmp4) boolean
    //typeof(tmp5) undefined
    //typeof(tmp6) object
    //typeof(tmp7) object
    //typeof(tmp8) object
    //typeof(tmp9) function
    //tmp9.name tmp9
    

null undefined 和布尔值

null 和 undefined 的区别

  • null 表示一个空对象,undefined 表示未定义;
  • null 转为数值的时候为 0, undefined 转为数值的实收为 NaN;

boolean

布尔值表示真和假,true 表示真,false 表示假;

下列运算符会返回布尔值:

  1. ! (not)
  2. 相等运算符: =, , !, !=
  3. 比较运算符: >=, <=, <,>

表示 false 的值

在自动数据转换中,下列值会表示 false:

  1. undefined
  2. null
  3. false
  4. 0
  5. NaN
  6. “” 或者 ‘’ 空字符串

其他的值都会被当成 true;

空数组 [] 和空对象 {} 对应的布尔值都是 true;

数值

在 js 中,所有的数值都是 64 位浮点数的形式进行存储的,也就是说在 js 底层,没有整数只有浮点数;

因为浮点数精度的问题,js 在进行浮点数运算的时候经常会出现问题:

console.log(0.1 + 0.2);
// 0.30000000000000004

进制

  • 十进制:没有前导,直接用数值表示
  • 八进制:有前缀 0o 或者 0O
  • 十六进制:有前缀 0x 或者 0X
  • 二进制:有前缀 0b 或者 0B

默认情况下,js 内部会将八进制,十六进制,二进制转为十进制;

NaN

NaN 是 js 中的特殊值,表示非数字 (Not a Number), 主要出现在字符串解析成数字出错的时候;

  1. NaN 不是独立的数据类型,它是一个特殊值,它的数据类型依然是 Number
  2. NaN 不等于任何值,包括它本身 (不等于本身可以用来检测某个值是否是 NaN)
  3. NaN 和任何数运算,得到的结果都是 NaN;

Infinity

Infinity 用来表示无穷,一般出现在两种场景下:

  1. 正数的数值太大,或者负数的数值太小;
  2. 非 0 的数除以 0, 得到 Infinity
  • js 中数值正向溢出或者负向溢出或者被 0 除都不会报错,所以单纯的数学运算几乎没有可能抛出异常
  • Infinity 大于一切数值 (除了 NaN); -Infinity 小于一切数值 (除了 NaN)
  • Infinity 和 NaN 比较,总是返回 false;

全局 api

parseInt(string[,radix])

parseInt(string[,radix]) 将字符串解析成数值;如果入参非字符串,则会调用入参的 toString 方法转换为字符串再进行转换;如果设置了第二个参数 radix, 则会将字符串按指定的 radix 进制转换成十进制;返回数值或者 NaN

var a = '0xf';
console.log(a, 16); // 将 16 进制的 0xf 转为十进制
// 15

parseFloat(string)

parseFloat(string) 将字符串入参解析成浮点数;返回浮点数或者 NaN ;

var a = "4.567";
console.log(parseFloat(a)); // 4.567

// 当入参有非法字符, 则只保留合法部分进行转换
var b = "4.567abcd";
console.log(parseFloat(b)); // 4.567

// 第二个小数点忽略
var c = "1.2.3";
console.log(parseFloat(c)); // 1.2

// 起始为非法字符则直接返回 NaN
var d = "aaa1.2"; // NaN
console.log(parseFloat(d));

isNaN()

判断某个入参是否是 NaN; 可以利用 NaN 的不等性来进行判断

var a = NaN;
if (a != a ){
    console.log("it is NaN");
}

isFinite()

判断某个入参是否是 Infinity;

字符串

  • 字符串和数组相似,都支持使用 [] 运算符来通过指定索引来获取值;
  • length: 可以获取字符串的长度
  • 字符串不能通过 [] 运算符和索引来修改字符串的值

字符集

js 中使用的字符集为 Unicode 字符集,所有的字符串都使用 Unicode 表示;

var f\u006F\u006F = 'abc';
console.log(f\u006F\u006F);

base64 转码

  • 浏览器:
    • btoa:任意值转为 base64 编码
    • atob:base64 值解码
    • 非 ASCII 码 (如中文) 要转码之后再 base64;
      • encodeURIComponent
      • decodeURIComponent
  • Nodejs
    • var base64encode = Buffer.from("js").toString("base64");
    • var base64decode = Buffer.from(base64encode,'base64').toString();

对象

  • 对象就是一组键值对 key-value 的集合,是一种无序的复合数据集合
  • 对象的每个键名又称为属性 property; 它的值可以是任意数据类型;
  • 如果一个对象的某个属性的值是函数,则通常将这个属性称为该对象的方法,可以像函数一样调用这个方法;
  • 属性可以动态创建,不必在对象声明的时候就全部定义;

对象引用

  • 如果不同的变量名指向同一个对象,那么他们都是对这个对象的引用;也就是说这些变量都指向了同一个内存地址,修改其中任意一个的值都会影响到其他的变量;
  • 如果取消某一个变量对于原对象的引用,不会影响到其他引用该对象的变量
  • 这种引用只局限于对象,如果两个变量指向同一个原始类型的值,这些变量都是对原始类型的值的拷贝,改变任意变量都不会影响其他变量

属性查看

Obejct.keys() 可以查看该对象自身的属性,继承来的属性无法查看

var a = {
    hello: function(){
        consoel.log("hi");
    },
    table: [1, 2, 3],
    name: "kevin",
    age: 21,
    married: false
}

console.log(Object.keys(a));
//[ 'hello', 'table', 'name', 'age', 'married' ]

属性删除

  • delete 用于删除对象的属性,删除成功后返回 true
  • 删除一个不存在的属性,delete 不会报错,而且返回 true
  • 只有一种情况,delete 命令会返回 false; 那就是删除一条存在的属性,但是这条属性被定义成不能删除;(定义该属性不能删除 defineProperty)
  • 只能删除对象自身的属性,继承来的属性不能删除
var a = {
    hello: function(){
        consoel.log("hi");
    },
    table: [1, 2, 3],
    name: "kevin",
    age: 21,
    married: false
}

delete a.age, delete a.hello;
console.log(Object.keys(a));
// [ 'table', 'name', 'married' ]

属性存在判断

  • in 运算符可以用于检查对象是否包含某个属性;检查的是键名,存在这个属性返回 true, 不存在则返回 false;
  • hasOwnProperty(): 判断某个属性是否是该对象自身的属性;
JSvar a = {
    hello: function () {
        consoel.log("hi");
    },
    table: [1, 2, 3],
    name: "kevin",
    age: 21,
    married: false
}
console.log(a.hasOwnProperty('table'));
if ("table" in a) {
    console.log("table is property of a");
}

属性遍历

  • for in 循环可以用于遍历对象的全部属性;不仅可以遍历对象自身的属性,还可以遍历对象继承来的属性;
  • 如果只想遍历对象自身的属性,可以配合 hasOwnProperty() 进行筛选
var a = {
    hello: function(){
        consoel.log("hi");
    },
    table: [1, 2, 3],
    name: "kevin",
    age: 21,
    married: false
}

for (const aKey in a) {
    // 使用 hasOwnProperty 进行筛选
    if (a.hasOwnProperty(aKey)) {
        console.log(aKey);
    }
}

函数

函数声明

js 中函数声明有三种方式

  1. 使用 function 申明 function a(){}
  2. 函数表达式 var a = function(){}
  3. Function 构造函数 var a = Function("a","b", "return a+b") 或者 var a = new Function("a","b", "return a+b") 这两种方式效果一样

函数是一等公民

js 将函数看成是一个值,与其他数据类型一样,凡是可以使用其他数据类型的地方都可以使用函数,例如:

  1. 可以将函数赋值给变量或者对象的属性
  2. 可以将函数作为参数传递给其他函数
  3. 可以将函数作为其他函数的返回值

函数变量名提升

js 中全局变量名存在变量提升,函数内部的局部变量也存在变量提升;

function outer() {
    console.log(a); // undefined 说明全局变量存在变量提升
    console.log(b); // undefined 说明局部变量存在变量提升
    var b = 2;
}
outer();
var a = 1;

js 中函数的声明也存在变量提升,可以先调用该方法,再定义该方法

b();
function b() {
    console.log("b called");
}

函数的属性和方法

  • name (属性):返回函数的名字
  • length (属性):返回函数预期传入的形参数量
  • toString () 方法:返回函数的字符串源码

函数作用域

  • 作用域 scope 是指变量存在的范围
    • es5 中 js 只有两个作用域
      • 全局作用域:变量在整个程序中一直存在,所有地方都可以读取到该变量
      • 函数作用域:变量只在函数内部存在
    • es6 中新增了块级作用域
  • 函数外部声明的变量就是全局变量,它可以在函数内部读取到;
  • 在函数内部声明的变量就是局部变量,函数外部无法读取;
  • 函数本身的作用域就是其声明时所在的作用域,与其运行时的作用域无关;
  • 函数内部声明的函数,作用域绑定函数内部 (闭包)

函数参数省略

js 中函数的参数不是必须的,允许省略函数的参数;

函数的 length 属性只和函数声明时形参的个数有关,和实际调用时传入的参数个数无关;

参数传递方式

  • 函数的参数如果是原始数据类型 (数值,字符串,布尔), 参数传递使用按值传递的方式,在函数内部修改参数的值不会影响函数外部
  • 如果函数的参数是复合数据类型 (数组,对象,其他函数), 参数的传递方式是按址传递,传入的是引用的地址,因此在函数内部修改参数,会影响到原始值;

arguments 对象

  • 因为 js 允许函数有不定数目的参数,所以需要在函数体的内部可以读取到所有参数,这就是 arguments 对象的由来
  • arguments 对象包含了函数运行时的所有参数,这是一个类数组对象,arguments[0] 就是第一个参数;
  • arguments 对象只能在函数内部使用
  • arguments.length 可以获取函数调用时入参的真正个数
  • arguments.callee 属性可以获取对应的原函数
  • arguments 对象是一个类数组对象,如果要让他使用真正的数组方法,需要将 arguments 转换成数组:
    • Array.prototype.slice.call(arguments)
    • 新建数组,遍历 arguments 将元素 push 到新数组中;

闭包

  • 要理解闭包首先要理解 js 的作用域;前面提到的 js 在 es5 中只有两种作用域:
    • 全局作用域
    • 函数作用域
  • 在函数的内部可以全局作用域的变量
  • js 中特有的链式作用域结构,子级会向上一级一级寻找所有父级的变量,父级的所有变量对于子级来说都是可见的,反之不成立;
var a = 1;
var b = 2;
function f() {
    var b = 3;
    console.log(a, b);
    function f1() {
        var b = 4;
        console.log(a, b);
    }
    f1();
}
f(); // 1 3// 1 4

链式作用域查找

子级会优先使用自己的作用域,如果变量存在则使用,不存在则会依次向上寻找,直至全局作用域;

闭包定义

  • 闭包可以简单理解成定义在一个函数内部的函数
  • 闭包最大的特点就是它可以记住自己诞生的环境,本质上,闭包就是将函数内部和函数外部连接起来的桥梁;
  • 闭包最大的用处有两个
    1. 可以直接读取到外层函数内部的变量
    2. 可以让这些变量始终保存在内存中,闭包让自己诞生的环境一直存在

通过闭包实现简单的计数器

function count() {
    var count = 0;
    function f() {
        count++;
        console.log("count", count);
    }
    return f;
}
f = count();
f();
f();
f();

立即调用函数表达式

js 中有三种立即调用函数的方式

  1. var f = function(){}();
  2. (function(){}())
  3. (function(){})()

通常情况下,只对匿名函数使用这种立即执行的表达式,这样有两个目的:

  1. 不必为函数命名,避免污染全局环境
  2. 立即调用函数的内部会形成单独的作用域,可以封装一些外部无法读取的私有变量

eval 命令

  • eval 可以接受一个字符串,并将字符串当做代码执行
  • eval 没有自己的作用域,都是使用当前运行的作用域,所以 eval 会修改当前作用域下的变量的值
  • eval 的本质是在当前作用域中,注入代码,经常用于混淆和反爬

eval 别名调用

eval 的别名调用在 nodejs 下无法跑通,需要在浏览器下运行;

需要注意,在 eval 通过别名调用的时候,作用域永远是全局作用域;

var a = 1;
var e = eval;
(function () {
    var a = 2;
    e("console.log(a);"); // eval 在别名调用的时候使用全局作用域
}())

数组

  • 数组 Array 是按次序排列的一组值
  • 每个值的位置都有对应的索引
  • 数组使用 来表示
  • 任何类型的数据都可以放入数组中
  • 本质上,数组是特殊的对象,typeof 查看数组的类型返回的是 object
  • Object.keys () 可以返回数组的键名 (索引)

数组的属性

  • length: 表示数组的元素个数,这个属性是可写的,可以直接修改数组的 length 属性,来实现清空数组或删除数组中元素的效果
  • 数组本质上是特殊的对象,支持使用点操作符对数组添加属性;
var a = 1;
var e = eval;
(function () {
    var a = 2;
    e("console.log(a);"); // eval 在别名调用的时候使用全局作用域
}
    ())
var a = [1, 1.1, true, {}, [], "hello", null, undefined];
console.log("a.length", a.length);
a.name = "add a.name property";
for (const aKey in a) {
    console.log(aKey, a[aKey]);
}
console.log("Object.keys(a)", Object.keys(a));
a.length = 0;
console.log("a", a);
console.log("a['name']", a['name']);
console.log("a.name", a.name);
console.log("a[0]", a[0]);

数组循环

  • for in 循环
  • for 循环
  • while 循环
  • forEach : 只有数组才有该方法;该方法接受一个回调函数,回调函数入参为 value 和 key;
var a = 1;
var e = eval;
(function () {
    var a = 2;
    e("console.log(a);"); // eval 在别名调用的时候使用全局作用域
}
    ())
var a = [1, 1.1, true, {}, [], "hello", null, undefined];
console.log("a.length", a.length);
a.name = "add a.name property";
for (const aKey in a) {
    console.log(aKey, a[aKey]);
}
console.log("Object.keys(a)", Object.keys(a));
a.length = 0;
console.log("a", a);
console.log("a['name']", a['name']);
console.log("a.name", a.name);
console.log("a[0]", a[0]);
var a = [1, 1.1, true, {}, [], "hello", null, undefined];

for infor(const aKey in a) {
    console.log("aKey:", aKey, "value:", a[aKey]);
}

console.log("-------------------------------")

forfor(var i = 0; i <= a.length; i++) {
    console.log("index:", i, "value:", a[i]);
}

console.log("-------------------------------")

whilevar index = 0;
while (index <= a.length) {
    console.log("index:", index, "value:", a[index]);
    index++;
}

console.log("-------------------------------")

forEacha.forEach(function (value, key) {
    console.log("key:", key, "value:", value);
})

数组空值

js 中的数组支持空值,出现空值时会占用该索引位,但是遍历的时候不会遍历该索引的值

var a = [1, 2, 3, , 5];
a.forEach(function (value, key) {
    console.log("key", key, "value", value);
})
// key 0 value 1
// key 1 value 2
// key 2 value 3
// key 4 value 5

类数组对象

  • 如果一个对象的所有键名都是正整数或者 0, 且有 length 属性,那么这个对象就是类数组对象
  • 典型的类数组对象有 arguments 对象,字符串,以及大部分的 dom 元素集
  • 数组的 slice 方法可以将类似数组的对象变成真正的数组 var arr = Array.prototype.slice.call(arrayLike);
  • 除了将类数组对象转成真正的数组,还可以使用 call () 将数组的方法直接放到类数组对象上使用 Array.prototype.forEach.call(arrayLike, function(){});

数据类型转换

自动数据类型转换

其他类型转字符串

+ 加号作为操作符,且操作数中含有字符串时,会自动将另一个操作数转为字符串;

规则如下:

  1. 字符串 + 基础数据类型:会直接将基础数据类型转为和字面量相同的字符串
  2. 字符串 + 复合数据类型:复合数据类型会先调用 valueOf() 方法,如果该方法返回基础数据类型则将其转为字符串,如果返回的是复合数据类型,则调用 toString() 方法,如果返回的是基础数据类型则将其转为字符串,如果不是则报错;
// 基础类型
// 自动数据类型转换
// + 字符串
// 1. 字符串 + 基础数据类型: 会直接将基础数据类型转为和字面量相同的字符串
var tmp1 = "" + 3;
console.log("tmp1", tmp1); // tmp1 "3"

var tmp2 = "" + true;
console.log("tmp2", tmp2); // tmp2 "true"

var tmp3 = "" + undefined;
console.log("tmp3", tmp3); // tmp3 "undefined"

var tmp4 = "" + null;
console.log("tmp4", tmp4); // tmp4 "null"

// 字符串+复合数据类型: 复合数据类型会先调用 valueOf 方法, 如果此方法返回的是引用类型, 则再调用 toString()方法, 最后将返回值转为字符串类型
var tmp5 = [1, 2, 3] + "";
console.log("tmp5", tmp5); // tmp5 1,2,3

var tmp6 = {} + "";
console.log("tmp6", tmp6); // tmp6 [object Object]

// 重写 toString 方法
var o = {
    toString: function () {
        return 1;
    }
}

var tmp7 = o + "";
console.log("tmp7", tmp7) // tmp7 "1"

// 重写 valueOf 方法
o.valueOf = function () {
    return 2;
}
var tmp8 = "" + o;
console.log("tmp8", tmp8); // tmp8 2

var a = {
    valueOf: function () {
        return {}
    },
    toString: function () {
        return "toString"
    }
};

console.log("" + a); // toString

其他类型转布尔值

数值转布尔值

数值在逻辑判断条件下会自动转成布尔值;±0 和 NaN 为 false, 其他数值都是 true;

if (0) {
    console.log("0 is true");
}else{
    console.log("0 is false");
}

if (NaN) {
    console.log("NaN is true");
}else{
    console.log("NaN is false");
}

if (-0) {
    console.log("-0 is true");
} else {
    console.log("-0 is false")
}

// 0 is false
// NaN is false
// -0 is false

字符串转布尔值

空字符串”” 或者’’为 false, 其他都是 true

undefined 和 null 转布尔值

undefined 和 null 转为布尔值都是 false

对象转布尔值

只有对象为 null 或者 undefined 时,转为布尔值才是 false; 其他情况下 (包括空对象 {} 和空数组 []) 转为布尔值都是 true;

var o = {};
if(o){
    console.log("{} is true");
}else{
    console.log("[] is true");
}
// {} is true ; 空数组[] 同理

其他类型转为数值

一元操作符 +- 都会触发其他类型转为数值;

数学运算符操作的两个操作数都不是字符串时,也会触发其他类型转为数值的操作;

转化规则如下:

  1. 字符串转数值:空字符串转为 0, 非空字符串转为对应的数字;不合法则返回 NaN;
  2. 布尔值转数值: true 为 1, false 为 0;
  3. null 转为数值为 0
  4. undefined 转数值为 NaN
  5. 对象转数值会先调用 valueOf() 方法,如果其返回值是基础数据类型,则将其转为数值返回,如果是复合数据类型,则会调用 toString() 方法,再将其转为数值,如果不合法则返回 NaN;

强制类型转换

  • Number (): 将其他类型转为数值;将对象转为数值时,会先调用对象的 valueOf() 方法,不满足条件再调用 toString() 方法
  • String (): 将其他类型转为字符串;将对象转为字符串时,会先调用对象的 toString() 方法,不满足条件时再调用对象的 valueOf() 方法;
  • Boolean (): 将其他类型转为布尔值

异常处理

Error 对象

Error 对象通常包含常用的三个属性,且必须包含 message 属性;

  1. name: 异常名
  2. message: 异常提示信息
  3. stack: 异常调用栈信息
var e = new Error("自定义异常触发");
e.name = "自定义异常名称";
console.log(e.name);
console.log(e.message);
console.log(e.stack);

/*
自定义异常名称
自定义异常触发
自定义异常名称: 自定义异常触发
    at Object.<anonymous> (/Users/zhangyang/codes/antiJs/js_learn/_03_基本数据类型(下)/tmp.js:201:9)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12)
    at internal/main/run_main_module.js:17:47
*/

try catch finally

手动抛出异常并捕获

try {
    var e = new Error("message 自定义异常");
    e.name = "自定义异常名称";
    throw e;
} catch (e) {
    console.log("e.name", e.name, "e.message", e.message);
    console.log("e.stack", e.stack);
}

throw

throw 语句用来抛出用户定义的异常,当前函数的执行将被停止,throw 之后的代码不会被执行;并且代码将会进入调用栈中第一个 catch 块中;如果没有 catch 块,程序将会终止;

try {
    console.log("before throw error");
    throw new Error("throw error");
    console.log("after throw error");
} catch (e) {
    console.log("catch error", e.message);
}
// before throw error
// catch error throw error

try/catch/finally

try 的三种声明形式:

  1. try…catch
  2. try…finally
  3. try…catch…finally

try/catch 主要用于捕获异常,try/catch 语句包含一个 try 块,和至少一个 catch 块或者一个 finally 块;

  • try: try 块中放入可能会产生异常的语句或者函数

  • catch: catch 块中包含要执行的语句,当 try 块中抛出异常时,catch 块会捕获这个异常,并执行 catch 块中的代码;如果 try 块中没有异常抛出,则 catch 块会被跳过;

  • finally: finally 块在 try 块和 catch 块之后执行,无论是否有异常产生,finally 块都会执行;当 finally 块中有异常产生时,会覆盖掉 try 块中的异常信息;

    try {
    
        try {
            throw new Error("error1");
        } finally {
            throw new Error("error2");
        }
    } catch (e) {
        console.log("e.message", e.message);
    }
    
    // e.message error2
    
  • 如果 finally 块中返回一个值,那么这个值将会作为整个 try/catch/finally 的返回值,无论在 try 和 catch 有没有 return, 都是返回 finally 中的值;

    function test() {
        try {
            throw new Error("error1");
            return 1
        }catch (e) {
            throw new Error("error2");
            return 2;
        }finally {
            return 3;
        }
    }
    
    console.log("test()", test());
    // test() 3
    

对象详解和 hook

Object 静态方法和实例方法

  • js 中所有其他对象都是继承自 Object ; 即所有对象都是 Object 的实例;
  • Object 的原生方法分为两类: Object 本身的方法和 Object 的实例方法
    1. Object 本身的方法:直接定义在 Object 上的方法,相当于 java 的静态方法
    2. Object 的实例方法:定义在 Object 的原型对象 Object.prototype 上的方法;相当于 java 中的动态方法,可以被 Object 对象直接使用

Object 的静态方法

所谓静态方法,就是设置在 Object 类上的方法;

  • Object.keys (): 遍历对象的属性,返回可枚举的属性名;这个方法只能查看对象自身的属性,继承来的属性无法查看
  • Object.getOwnpeopertyNames (): 遍历对象的属性,可以返回不可枚举的属性名;
  • Object.getOwnPropertyDescriptors (): 获取对象所有属性的描述对象;
  • Object.defineProperty (): 通过描述对象,定义某个属性,可以重写 get 和 set 方法来进行 hook
  • Object.defineProperties (): 通过描述对象,定义多个属性,可以重写 set 和 get 方法来进行 hook
  • Object.getPrototypeOf (): 返回参数对象的原型,可以用于环
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嗨!对于逆向学习汇编语言的学习笔记,我可以给你一些基本的指导。首先,汇编语言是一种低级语言,它与计算机的底层硬件密切相关。逆向工程则是通过分析和理解已编译的程序来获取程序的内部信息。 以下是一些学习汇编语言和逆向工程的建议: 1. 学习基础知识:了解计算机体系结构、寄存器、内存和指令集等基础概念是必要的。可以先阅读相关的书籍或在线教程,掌握这些基本概念。 2. 掌握汇编语言的语法和指令集:每种计算机体系结构都有自己的汇编语言语法和指令集。选择一种你感兴趣的体系结构(如x86、ARM等),并学习它的汇编语言。 3. 练习编写和调试汇编代码:通过编写简单的汇编代码来熟悉语法和指令集。使用调试器来单步执行代码并观察寄存器和内存的变化。 4. 分析已编译程序:选择一个目标程序进行逆向分析。使用反汇编器将程序转换为汇编代码,并分析代码的逻辑和功能。这有助于理解程序的结构和运行过程。 5. 使用调试器进行动态分析:通过调试器来动态地执行程序,并观察程序在运行时的行为。使用断点、内存查看器和寄存器查看器等工具来分析程序的状态和数据。 6. 学习逆向工程工具和技术:了解常用的逆向工程工具和技术,如IDA Pro、OllyDbg、Ghidra等。掌握这些工具的使用可以提高你的逆向分析能力。 7. 参考优秀资源:阅读与逆向工程和汇编语言相关的书籍、论文和博客,关注相关的社区和论坛。与其他逆向工程师交流经验也是很有帮助的。 记住,逆向工程是一个需要耐心和实践的过程。持续学习和实践将帮助你提高逆向分析的技能。祝你在学习汇编语言和逆向工程的过程中取得好成果!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值