web逆向经验

发布时间:2023年12月22日

一、JS逆向调试流程

  1. 如果网页有跳转,必须勾选 preservelog 防止丢包
  2. 看一下有没有框架 右键查看框架源代码(弹出式登陆界面)
  3. 登陆尽量使用错误密码 防止跳转
  4. 查看关键登陆包 分析哪些参数是加密的
  5. 使用别的浏览器分析哪些参数是固定的值
  6. 初步猜测加密方法
  7. 搜索,直接搜索参数,例如pwd ,Encrypt,decrypt
  8. 密码框地方右键 检查 查看 id name type
  9. 找到加密的地方(重点)(一般debug)
  10. 找出所有的加密代码
  • 从最后一步开始写起,缺啥找啥
  • 如果找的是函数的话 search 要带上 function xxx
  • 如果看到加密的地方有个类,并且之后是用 prototype 把方法加在原生对象上的话,要把 所有加在原生对象上的方法都找出来
  • 函数找多了没关系,只要不报错不会影响结果,但是不能找少了

二、加密破解

  • 始于前端js对密码加密实现的需要,目前使用最多是AES、RSA、MD5,当然这三个的嵌套和混合使用情况也比较多
  • 发现有加密时,搜索encrypt,JSON.parse,??JSON.stringify,??CryptoJS 等相关
  • 关于算法总结:
  1. 对称加密(加密解密密钥相同):DES、DES3、AES
  2. 非对称加密(分公钥私钥):RSA
  3. 信息摘要算法/签名算法:MD5、HMAC、SHA
  4. 前端实际使用中MD5、AES、RSA使用频率是最高的
  5. 几种加密方式配合次序:采用非对称加密算法管理对称算法的密钥,然后用对称加密算法加密数据,用签名算法生成非对称加密的摘要
  6. DES、DES3、AES、RSA、MD5、SHA、HMAC传入的消息或者密钥都是bytes数据类型,不是bytes数据类型的需要先转换;密钥一般是8的倍数

参考项目

三、有eval语句体的,直接把eval改为console.log,在控制台输出查看

四、补环境

  1. 什么是 “补浏览器环境”
  • 浏览器环境: 是指JS代码在浏览器中的运行时环境,它包括V8自动构建的对象(即ECMAScript的内容,如Date、Array),浏览器(内置)传递给V8的操作DOM和BOM的对象(如document、navigator)
  • Node环境:是基于V8引擎的Js运行时环境,它包括V8与其自己的内置API,如fs,http,path;

Node环境 与 浏览器环境 的异同点可以简单概括如图:
在这里插入图片描述
补浏览器环境” 其实是补浏览器有 而Node没有的环境,即补BOM和DOM的对象

  • 关于bom对象和dom对象:
    在这里插入图片描述
  1. 为什么要 “补浏览器环境”?

对于逆向老手而言,“补环境” 这个词不会陌生,当我们每次把辛辛苦苦扣出来的 “js加密算法代码”,并且放在浏览器环境中能正确执行后,就需要将它放到Node环境 中去执行,而由于Node环境与浏览器环境之间存在差异,会导致部分JS代码在浏览器中运行的结果 与在node中运行得到的结果不一样或者报异常,从而影响我们最终逆向成果;eg:

function decrypt() {
    document = false;
    var flag = document?true:false;
    if (flag) {
        return "正确加密"
    } else {
        return "错误加密";
    }
}

在浏览器环境运行时 flag为true,然后得到正常结果;
在Node环境运行时 flag为false,然后得到错误结果;

所以我们需要 “补浏览器环境”,使得扣出来的 “js加密算法代码” 在Node环境中运行得到的加密值,与其在 浏览器环境中运行得到的加密值一致。 即对于这段 “js加密算法代码” 而言,我们补出来的环境与浏览器环境一致。

  1. 怎么 “补浏览器环境”?

要想 “补浏览器环境”,首先我们得知道 “js加密算法代码” 到底使用了哪些浏览器环境API,然后再对应去补上这些环境
那么我们该如何监测 “js加密算法代码” 对浏览器环境API的使用呢?毫无争议:使用Proxy来监测浏览器环境API的使用,辅助补浏览器环境

Proxy是ES6提供的代理器,用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。它可以代理任何类型的对象,包括原生数组,函数,甚至另一个代理;拥有递归套娃的能力

也就是说我们代理某个对象后,我们就成了它的中间商,任何JS代码对它的任何操作都可以被我们所拦截

  1. 基于Proxy的特性,衍生了两种补环境思路:
  • 递归嵌套Proxy以此来代理浏览器所有的BOM、DOM对象及其属性,再配合node vm2模块提供的纯净V8环境,就相当于在node中,对整个浏览器环境对象进行了代理,JS代码使用任何浏览器环境 api都能被我们所拦截。然后我们针对拦截到的环境检测点去补。
  • 搭建补环境框架,用JS模拟浏览器基于原型链去伪造实现各个BOM、DOM对象,然后将这些JS组织起来,形成一个纯JS丐版浏览器环境,我们补的纯JS丐版浏览器环境越完善,就越接近真实浏览器环境,能通杀的js环境检测就越多。最终完美通杀所有JS环境检测
  • 第一种思路虽然实现简单,主要是对Proxy拦截器的使用,但是具备的环境监测能力有限,对较复杂的原型链等难以监测,即使是二次开发也上限不高;并且遇到JS使用了很多环境时手补也相当麻烦;
  • 第二种思路虽然实现较为复杂,但是上限极高,且可以完美兼容第一种思路,具备可成长的通杀潜质。

所以业内补环境框架几乎都是基于第二种思路,先搭建一个补环境框架的骨架,将常见浏览器环境BOM、DOM对象补齐,如:window、location、Document、navigator等,等空闲时或工作遇到其他浏览器环境BOM、DOM对象,再将它补进来。补的越完善,我们能通杀JS环境检测越多。

  • 优点:
  • 补的越完善,能通杀JS环境检测越多。
  • 一键运行输出目标JS中所有环境检测点;
  • 生成的最终代码可直接用于生产环境(可直接供nodejs、v8使用); 告别玄学补环境,不再一行行去debugger,极大提高工作效率。
  • 可以在Chrome浏览器进行无浏览器环境调试。
  1. 常见的补环境方式
  • window is not defined;

修改为: window = global或者,var window = {}

  1. ASN1 is not define
尝试window = global
  1. navigator is not defined

修改为: var navigator = {}

  1. Cannot read property ‘userAgent’ of undefined

navigator.userAgent = ‘xxxx’

  1. Cannot read property ‘body’ of undefined:

这个是 document里面的。

修改为:document.body = {xxx(具体的某个参数)}

  1. Cannot read property ‘x’ of undefined

缺少函数,具体缺少什么,可根据调试来添加。

  1. Cannot read property ‘href’ of undefined
是 location.href
修改为:
window.location = {
"xxxx": {},
"href": "https://www.xxxx.com/",
(其他参数保持)
}
  1. Cannot read property ‘length’ of undefined
具体原因是取值的时候,取到的是null。。。

具体调试可得知。
  1. Cannot read property ‘cookie’ of undefined
var document = {
cookie:"xxxxxx"
}
  1. Object.getOwnPropertyDescriptor
var Navigator = function() {};
Navigator.prototype = {"platform": "win32"};
navigator = new Navigator();

在这里插入图片描述

  1. 补齐window.localStorage:
window.localStorage = {
    removeItem: function (key) {
        delete this[key]
    },
    getItem: function (key) {
        return this[key] ? this[key]: null;
    },
    setItem: function (key, value) {
        this[key] = "" + value;  // 将数字转为字符串
    },
};
  1. .document补齐
location = {
    "href": "https://www.w3school.com.cn/jsref/prop_anchor_href.asp",
    "origin": "https://www.w3school.com.cn",
    "protocol": "https:",
}
document = {
    createElement: function () {
        var loc = {
            href: ""
        };
        var temp_href = loc.href;
        Object.defineProperty(loc, 'href', {
            // Hook loc.href,当为其赋值时,按下面的规则强制改变
            get: function () {
                return temp_href
            },
            set: function (val) {
                if (val.indexOf('http://') === 0 || val.indexOf('https://') === 0) {
                    // 1.当值为http://或https://开头时,即为该值
                    temp_href = val;
                } else if (val.indexOf('//') === 0) {
                    // 2.当值为//开头时,即为location.protocol加上该值
                    temp_href = location.protocol + val;
                } else if (val.indexOf('/') === 0) {
                    // 3.当值为/开头时,即为location.origin加上该值
                    temp_href = location.origin + val;
                } else {
                    // 4.除以上3种情况,即为location.href中最后一个/之前的值加上该值
                    var s = location.href
                    temp_href = s.substring(0, s.lastIndexOf("/")+1) + val
                }
                return temp_href
            }
        });
        return loc;
    }
}

五、控制流平坦化

将顺序执行的代码混淆成乱序执行,并加以混淆,以下两段代码的执行结果是相同的

// 正常形态
function test(a){
  var b = a;
  b += 1;
  b += 2;
  b += 3;
  b += 4;
  return a + b
}
 
// 乱序形态
//(这里比较简单,在很多加密网站上case 后面往往不是数字或字符串,而是类似 YFp[15][45][4]这样的对象,相当恶心)
function test1(a){
  var arr = [1,2,3,4,5,6]
  for(var i = 0, i < arr.lenght, i++){
    switch (arr[i]) {
      case 4:
        b += 3;
        break;
      case 2:
        b += 1;
      break;
      case 1:
        var b = a;
      break;
     case 3:
        b += 2;
      break;
      case 6:
        return a + b
      case 5:
        b += 4;
      break;
    }
  }
}
// 结果都是30 但是test1看着费劲
console.log(test1(10));
console.log(test(10));

六、去除无限debug

//去除无限debugger
Function.prototype.__constructor_back = Function.prototype.constructor;
Function.prototype.constructor = function() {
    if(arguments && typeof arguments[0]==='string'){
        //alert("new function: "+ arguments[0]);
        if("debugger" === arguments[0]){
            //arguments[0]="console.log(\"anti debugger\");";
            //arguments[0]=";";
            return
        }
    }
   return Function.prototype.__constructor_back.apply(this,arguments);
}

未完待续

文章来源:https://blog.csdn.net/cristianoxm/article/details/135153328
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。