普通视图

发现新文章,点击刷新页面。
昨天以前掘金专栏-公众号iOS逆向

iOS小技能:1、yalu102 激活了之后,无法连接ssh的解决方案 2、Reveal的基本使用3、Passionfruit 的实现原理

本文正在参加「金石计划」

前言

  1. 无法连接ssh的解决方案
  2. Reveal的安装、配置及使用
  3. Passionfruit 的实现原理
  4. Mac上Nodejs环境搭建

I 使用yalu102 激活了之后,无法连接ssh的解决方案

yalu102此次越狱工具默认安装了 SSH.采用了 Dropbear 取代 Openssh。#/usr/local/bin/dropbear

-部署安装使用yalu102时,修改dropbear.plist的信息: ProgramArguments的127.0.0.1:22 直接改为22。

如果部署完成,直接修改沙盒的信息的话,记得重启设备。

1.1 获取直接修改对应的配置信息

  • ps -e |grep yalu*
iPhone:~ root# ps -e |grep yalu*
 1174 ??         0:00.80 /var/containers/Bundle/Application/B831448D-BCD0-4F29-BDA6-9FC03903D30C/yalu102.app/yalu102
  • plutil plist 内容
iPhone:/var/containers/Bundle/Application/B831448D-BCD0-4F29-BDA6-9FC03903D30C/yalu102.app root# plutil dropbear.plist
{
   KeepAlive = 1;
   Label = ShaiHulud;
   Program = "/usr/local/bin/dropbear";
   ProgramArguments =     (
       "/usr/local/bin/dropbear",
       "-F",
       "-R",
       "-p",
       22
   );
   RunAtLoad = 1;
}
  • dropbear 参数
 iPhone:/var/containers/Bundle/Application/B831448D-BCD0-4F29-BDA6-9FC03903D30C/yalu102.app root# ps -e |grep dropbear
  228 ??         0:00.05 /usr/local/bin/dropbear -F -R -p 22

1.2 利用wget 安装scp( 解决:sh: scp: command not found)

#两台服务器都要安装scp才能传文件
#wget + 空格 + 要下载文件的url路径
# cydia里面安装wget
#  安装scp,默认安装在当前目录
wget mila432.com/scp
ldid -S scp
# chmod +x scp
chmod 777 scp
mv scp /usr/bin/scp
# 或者使用curl: curl -O mila432.com/scp ./scp
  • dyld: Library not loaded: /usr/lib/libssl.0.9.8.dylib 重新安装openssl
  • 浏览器下载scp
 find . -name "scp" 

II Reveal的安装、配置及使用

目前自己经常使用的是AFlexLoader

2.1. Reveal Loader安装


首先我们打款越狱设备的Cydia,然后在搜索中输入Reveal Loader,并且进行安装即可,下方是安装后的效果。这一步比较简单,安装后重启SpringBoard即可。

iPhone:~ root# cd /Library/RHRevealLoader
-sh: cd: /Library/RHRevealLoader: No such file or directory

进行第二步骤,导入libReveal.dylib

2.2.导入libReveal.dylib


Mac上的Reveal自带了两个库,一个是libReveal.dylib,一个是Reveal.framework。 在未越狱的设备上使用的是后者,本文使用的是前者。

这两个文件位于Reveal中的iOS Library中。Reveal菜单->Help->Show Reveal Library in Finder ->iOS Library。通过上述目录就可以找到我们需要的文件。

 /Users/devzkn/Downloads/kevin-software/ios-Reverse_Engineering/Reveal/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib

mkdir

devzkndeMacBook-Pro:python-client devzkn$ ssh iphone
iPhone:~ root# mkdir /Library/RHRevealLoader
iPhone:~ root# cd /Library/RHRevealLoader
iPhone:/Library/RHRevealLoader root

scp

devzkndeMacBook-Pro:python-client devzkn$ scp  /Users/devzkn/Downloads/kevin-software/ios-Reverse_Engineering/Reveal/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib iphone:/Library/RHRevealLoader/

iPhone:~ root# ls -l /Library/RHRevealLoader/libReveal.dylib
-rwxr-xr-x 1 root admin 3850232 Oct 19 16:17 /Library/RHRevealLoader/libReveal.dylib

2.3、Reveal的使用


  • 选择Reveal的App

在设置中找到Reveal的配置项,在该配置项中我们可以去选择要Reveal的App, 当然对于越狱手机,手机上安装的所有App都可以Reveal。当然也包括从AppStore下载的,也包括iOS系统自带的

  • 查看app的UI层级

Mac上Reveal查看设备上App的UI层级时是不需要使用USB进行连接的,但要保证你的iOS设备与你的Mac在同一个局域网内

  • 注意事项

记得打开对应的app,保证你查看的app 处于运行状态

III Passionfruit 的实现原理

  • 我在使用Passionfruit 的时候,安装步骤碰到的问题是fatal error: 'frida-core.h' file not found,具体的请看Q&A。

  • 安全审计的工具 我觉得iNalyzer 已经过时了,推荐这款Passionfruit;

  • Passionfruit 通过frida注入代码到目标应用实现了个“动态分析iOS应用”的图形界面。

3.1 实现原理

Passionfruit 通过 frida 注入代码到目标应用实现功能,再通过 node.js 服务端消息代理与浏览器通信,用户通过访问网页即可对 App 实现常规的检测任务。 在这里插入图片描述

3.2 安装

3.2.1 安装前准备

  • brew install libimobiledevice
devzkndeMacBook-Pro:passionfruit devzkn$ brew install libimobiledevice

  • brew install yarn
devzkndeMacBook-Pro:passionfruit devzkn$  brew install yarn

  • install npm
brew install npm

3.2.2 运行 npm install 根据 package.json 文件安装依赖。

  • save frida then $ npm install
devzkndeMacBook-Pro:passionfruit devzkn$ npm install
> Passionfruit@0.0.3 postinstall /Users/devzkn/code/demo/passionfruit
> cd gui && (yarn || npm install)
yarn install v1.3.2
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
✨  Done in 231.08s.
up to date in 235.627s

  • npm run build
devzkndeMacBook-Pro:passionfruit devzkn$ npm run build

> Passionfruit@0.0.3 build /Users/devzkn/code/demo/passionfruit
> frida-compile agent -o _agent.js && cd gui && (yarn run build || npm run build)

yarn run v1.3.2

  • npm start
devzkndeMacBook-Pro:passionfruit devzkn$ npm start

> Passionfruit@0.0.3 start /Users/devzkn/code/demo/passionfruit
> cross-env NODE_ENV=production node .

listening on http://localhost:31337

3.3 Q&A

  • prebuild-install http 404 https://github.com/frida/frida/releases/download/10.6.13/frida-v10.6.13-node-v59-darwin-x64.tar.gz
devzkndeMacBook-Pro:passionfruit devzkn$ npm install --save frida@latest

> frida@10.6.28 install /Users/devzkn/code/demo/passionfruit/node_modules/frida
> prebuild-install || node-gyp rebuild

prebuild-install info begin Prebuild-install version 2.3.0
prebuild-install info looking for local prebuild @ prebuilds/frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install info looking for cached prebuild @ /Users/devzkn/.npm/_prebuilds/https-github.com-frida-frida-releases-download-10.6.28-frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install http request GET https://github.com/frida/frida/releases/download/10.6.28/frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install http 200 https://github.com/frida/frida/releases/download/10.6.28/frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install info downloading to @ /Users/devzkn/.npm/_prebuilds/https-github.com-frida-frida-releases-download-10.6.28-frida-v10.6.28-node-v59-darwin-x64.tar.gz.47369-6d6afc3ef5581.tmp
prebuild-install info renaming to @ /Users/devzkn/.npm/_prebuilds/https-github.com-frida-frida-releases-download-10.6.28-frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install info unpacking @ /Users/devzkn/.npm/_prebuilds/https-github.com-frida-frida-releases-download-10.6.28-frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install info unpack resolved to /Users/devzkn/code/demo/passionfruit/node_modules/frida/build/Release/frida_binding.node
prebuild-install info unpack required /Users/devzkn/code/demo/passionfruit/node_modules/frida/build/Release/frida_binding.node successfully
prebuild-install info install Successfully installed prebuilt binary!
+ frida@10.6.28
added 32 packages in 18.574s

  • Unable to launch iOS app: timeout

启动应用程序失败之后,装置就重启了。这个问题 有点类似Failed to spawn: unable to launch iOS app: timeout

临时解决方式: 手动启动app ,还是可以正常分析的

更多具体的分析请看这里: frida Failed to spawn 解决方案

IV Mac上Nodejs环境搭建

  1. 安装包安装:nodejs下载网址 http://nodejs.cn/download/
  2. 二进制安装:
brew install nodejs

brew install npm

see also

修改汇编的方式: 选中行,选择菜单栏的Modify > Assemble Instruction...,将jne修改成je,然后点击Assemble and Go Next。

公众号:iOS逆向

JavaScript预备知识

本文正在参加「金石计划」

前言

一个有具体功能的完整网页,一般由3部分组成:

  1. HTML(内容和结构): HyperText Markup Language,超文本标记语言。用来结构化网页内容并赋予内容含义,例如定义段落、标题和数据表,或在页面中嵌入图片和视频。

  2. css(样式): Cascading Style Sheets层叠样式表是一种样式规则语言,允许我们精确地设计HTML的样式,例如设置背景颜色和字体,在多个列中布局内容。

  3. JavaScript(交互效果) :JavaScript 是一种符合ECMAScript规范的脚本编程语言,可以用来创建动态更新的内容,控制多媒体,制作图像动画。

脚本语言是为了缩短传统编程语言从编写-编译-运行这个过程而开发的一种简单类型语言。

I 预备知识

1.1 术语:解释(interpret)和 编译 (compile)

编译原理 : https://blog.csdn.net/z929118967/article/details/123778003 在解释型语言中,代码自上而下运行,且实时返回运行结果。

js代码由浏览器执行前,不需要将其转化为其他形式,代码将直接以文本格式(text form)被接收和处理。

编译型语言需要先将代码转化(编译)成另一种形式才能运行。

C++,Objective C都是编译语言,必须先通过编译器生成机器码,然后才能由计算机运行。

Objective-C与swift都采用Clang作为编译器前端,编译器前端主要进行语法分析,语义分析,生成中间代码,在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。

在这里插入图片描述 编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化,根据不同的系统架构生成不同的机器码。

在这里插入图片描述

1.2 语言特点

  1. JavaScript 是轻量级解释型语言。

浏览器接受到 JavaScript 代码,并以代码自身的文本格式运行它。

技术上,几乎所有 JavaScript 转换器都运用了一种叫做即时编译(just-in-time compiling)的技术;当 JavaScript 源代码被执行时,它会被编译成二进制的格式,使代码运行速度更快。尽管如此,JavaScript 仍然是一门解释型语言,因为编译过程发生在代码运行中,而非之前。

JavaScript能被浏览器进行解释,是一种解释性语言。受浏览器的影响,在不同的浏览器可能表现的效果不一样,存在浏览器差异。

  1. 与大多数编程语言不同,JavaScript 没有输入或输出的概念。它是一个在宿主环境(host environment)下运行的脚本语言,任何与外界沟通的机制都是由宿主环境提供的。

浏览器是最常见的宿主环境,但Node.js 的服务器端环境中也包含 JavaScript 解释器,所以JavaScript 也可用作服务器端语言。

  1. JavaScript的语法来源于 Java 和 C,所以这两种语言的许多语法特性同样适用于 JavaScript。
  2. JavaScript 是一种“动态类型语言”(弱类型数据语言),这意味着不需要指定变量将包含什么数据类型,如果你声明一个变量并给它一个带引号的值,浏览器就会知道它是一个字符串:
let myString = 'Hello';
//提供了一个函数typeof用于检测数据属于哪个类型
//1.typeof 变量名
//2.typeof(变量名)
typeof myString;
  1. JavaScript 通过原型链而不是类来支持面向对象编程 ,JavaScript 同样支持函数式编程。

函数也可以被保存在变量中,并且像其他对象一样被传递。


document.querySelector('html').onclick = function() {
    alert('别戳我,我怕疼。');
}

document.querySelector('html').addEventListener('click', () => {
  alert('别戳我,我怕疼。');
});

//Longhand
function add(num1, num2) {
   return num1 + num2;
}
//Shorthand 箭头函数
const add = (num1, num2) => num1 + num2;
//`匿名函数`: 因为它没有名字,匿名函数还有另一种我们称之为`箭头函数`的写法,箭头函数使用` () => `代替 `function ()`:

函数式编程思想:把操作尽量写成一系列嵌套的函数或者方法调用。

函数式编程特点:每个方法必须有返回值(本身对象),把函数或者Block当做参数,block参数(需要操作的值)block返回值(操作结果)

iOS小技能:链式编程在iOS开发中的应用https://blog.csdn.net/z929118967/article/details/75219317

1.3 JavaScript的使用场所

使用场所:任何的HTML页面、所有的动态页面,通过 DOM API动态修改 HTML 和 CSS 来更新用户界面(user interface)。

没有动态更新内容的网页叫做“静态”页面,所显示的内容不会改变。

  1. 前端验证,通过验证提高数据的完整性以及安全性。
<input type="submit" value="确定" class="guessSubmit">

const guessSubmit = document.querySelector('.guessSubmit');
guessSubmit.addEventListener('click', checkGuess);
function checkGuess() {
//...
}
  1. 操纵html元素,响应用户的各种操作,提高用户体验性。
<input type="text" id="guessField" class="guessField">

const guessField = document.querySelector('.guessField');
  guessField.focus();//让光标在页面加载完毕时自动放置于 <input> 输入框内,这意味着玩家可以马上开始第一次猜测,而无需点击输入框。提高了可用性,为使用户能投入游戏提供一个良好的视觉线。
  1. ajax核心技术之一

ajax: 在浏览器中运行的js脚本,通过http请求异步地访问服务器组件,服务器组件返回xml文件或者json格式的数据,js接收后通过解析xml或json来局部刷新页面,提高用户体验。

  1. 获取浏览器的一些相关信息

1.4 脚本调用策略

HTML 元素是按其在页面中出现的次序调用的,如果用 JavaScript 来管理页面上的元素(更精确的说法是使用 文档对象模型 DOM),若 JavaScript 加载于欲操作的 HTML 元素之前,则代码将出错。

  1. 内部 JavaScript的解决方案
//监听浏览器的 "DOMContentLoaded" 事件,即 HTML 文档体加载、解释完毕事件
//可能会带来显著的性能损耗
document.addEventListener("DOMContentLoaded", function() {
//  . . .
});
  1. 外部JavaScript的解决方案(推荐)

async 属性可以解决调用顺序问题,它告知浏览器在遇到<script>元素时不要中断后续 HTML 内容的加载。

不依赖于本页面的其它任何脚本时,async 是最理想的选择。

<script src="script.js" async></script>

defer 属性,脚本将按照在页面中出现的顺序加载和运行:

<!--添加 defer 属性的脚本将按照在页面中出现的顺序加载-->
<script defer src="js/vendor/jquery.js"></script>

<script defer src="js/script2.js"></script>

<script defer src="js/script3.js"></script>

脚本调用策略小结:

  • 如果脚本无需等待页面解析,且无依赖独立运行,那么应使用 async。
  • 如果脚本需要等待页面解析,且依赖于其它脚本,调用这些脚本时应使用 defer,将关联的脚本按所需顺序置于 HTML 中。

II 应用程序接口(Application Programming Interfaces)

在这里插入图片描述

  1. 第三方 API 并没有默认嵌入浏览器中,一般要从网上取得它们的代码和信息,比如地图 API 可以在网站嵌入定制的地图。

  2. 浏览器 API 内建于 web 浏览器中,它们可以将数据从周边计算机环境中筛选出来,还可以做实用的复杂工作,比如DOM API。

  • 文档对象模型 API(Document Object Model Application Programming Interfaces ) 能通过创建、移除和修改 HTML,为页面动态应用新样式等手段来操作 HTML 和 CSS。
  • 地_理位置 API(Geolocation API) 获取地_理信息。
  • 画布(Canvas) 和 WebGL API 可以创建生动的 2D 和 3D 图像。
  • HTMLMediaElementWebRTC 等影音类 API 。

2.1 BOM(Browser Object Model,浏览器对象模型)

整个浏览器窗口是一个顶层window对象

  1. 函数 alert() 警告框 prompt() 对话框 confirm() 确认框 window.open("URL"); setTimeout();超时之后调用目标函数 clearTimeout();超时之后清除目标函数 focus() 获得焦点 setInterval(,) (以毫秒计)调用执行函数/表达式 setInterval(code,millisec[,"lang"]) clearInterval() 取消对 code 的周期性执行,由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。
//setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
//setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。
window.setInterval(function() {}, 18000)//18S

前端小技能:利用action-type按钮事件实现批量删除 https://blog.csdn.net/z929118967/article/details/123222483

  1. 浏览器对象 Navigator对象
  1. 属性: appName、 appVersion 、 History 历史记录对象
  2. 函数:go(url);
  1. 地址栏对象 Location

属性:href 通过改变地址栏访问目标地址

  1. 文档对象 Document

2.2 DOM(Document Object Model,文档对象模型)

由W3C定义的一组规范一组API 用来操作HTML对象

  1. 直接获得标签对象

1) document.getElementById("id属性值") 通过ID来获得对应的标签对象

    <iframe id="ifr" width=400 height=500></iframe>
        document.getElementById("ifr").src = url[j];

2) document.getElementsByTagName("标签名称") 根据标签名称来获得一组标签,返回数组对象 3) document.getElementsByName("name属性值");根据name属性值来获得一组标签,返回数组对象

  1. 间接获得标签对象

1) 父标签.childNodes 获得当前标签的所有孩子节点,返回数组对象 2) 父标签.firstChild 获得第一个孩子节点 3) 父标签.lastChild 获得最后一个孩子节点 4) 标签.nextSibling 获得下一个兄弟节点 5) 标签.previousSibling 获得前一个兄弟节点

  1. 创建节点对象

1) document.createElement("标签名称") 创建一个对应的标签对象 2) document.createTextNode("文本值"); 创建一个文本节点对象

  1. 操作标签

1) 父标签.appendChild(子节点); 将一个标签追加到父标签当中 2) 父标签.removeChild(子节点); 删除子节点 3) 父标签.insertBefore(newElement,targetElement); 在目标元素之前插入一个新元素 4) 父标签.replaceChild(newElement,oldElement); 用新元素替换掉旧的元素

  1. 其他操作

1)标签.style.样式属性 = 属性值; h1.style.backgroundColor = "red"; 2) 标签.innerHTML 获得或者设置元素的标签体 3) 文本节点.nodeValue 获得文本节点值 4) 标签.parentNode 获得父节点 5) this 表示当前标签对象

see also

公众号:iOS逆向

demo: https://codepen.io/zhangkn/pen/BaLyerq?editors=1111

JavaScript小技能: Ajax

本文正在参加「金石计划」

I 术语

1.1 术语:箭头函数

匿名函数: 因为它没有名字

//Longhand
function add(num1, num2) {
   return num1 + num2;
}

匿名函数还有另一种我们称之为箭头函数的写法,箭头函数使用() =>代替 function ()

//Shorthand 箭头函数
const add = (num1, num2) => num1 + num2;

1.2 术语:回调函数和Promise链

  1. 回调函数是一个被传递到另一个函数中的会在适当的时候被调用的函数,如事件处理程序就是一种特殊类型的回调函数。

由于嵌套回调导致处理错误变得非常困难,代码也更难阅读和调试,所以JavaScript 中的异步编程是基于 Promise实现。

  1. Promise是一个由异步函数返回的可以向我们指示当前操作所处的状态的对象。在基于 Promise 的 API 中,异步函数会启动操作并返回 Promise 对象。然后你可以将处理函数附加到 Promise 对象上,当操作完成时(成功或失败),这些处理函数将被执行。

在 Promise 返回给调用者的时候,操作往往还没有完成,但 Promise 对象可以让我们操作最终完成时对其进行处理(无论成功还是失败)。

例子:fetch() 一个基于 Promise 的、用于替代 XMLHttpRequest 的方法。

const fetchPromise = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');

console.log(fetchPromise);
//将一个处理函数传递给 Promise 的 then() 方法。
fetchPromise.then( response => {
  console.log(`已收到响应:${response.status}`);
});

console.log("已发送请求……");

"pending" 状态意味着操作仍在进行中。

  1. Promise 链:链式使用 Promise
const fetchPromise = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');

fetchPromise
  .then( response => {
    return response.json();//json() 也是异步的,response.json() 返回的是 Promise对象
  })
  .then( json => {
    console.log(json[0].name);
  });

Promise 对象提供了一个 catch() 方法来支持错误处理。当异步操作成功时,传递给 then() 的处理函数被调用,而当异步操作失败时,传递给 catch() 的处理函数被调用。

如果将 catch() 添加到 Promise 链的末尾,它就可以在任何异步函数失败时被调用。于是,我们就可以将一个操作实现为几个连续的异步函数调用,并在一个地方处理所有错误。

const fetchPromise = fetch('bad-scheme://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');

fetchPromise
  .then( response => {
    if (!response.ok) {
      throw new Error(`HTTP 请求错误:${response.status}`);
    }
    return response.json();
  })
  .then( json => {
    console.log(json[0].name);
  })
  .catch( error => {
    console.error(`无法获取产品列表:${error}`);
  });

1.3 JSON(JavaScript Object Notation)

是一种轻量级的数据交换格式。它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。

  1. JSON用于在网站上表示和传输数据
////通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。
//创建一个新的 XMLHttpRequest 并监听它的 loadend 事件

request.open('GET', requestURL);
request.responseType = 'text'; // now we're getting a string!
request.send();
//以事件处理程序属性形式关联事件处理器
request.onload = function() {
  var superHeroesText = request.response; // get the string from the response
  var superHeroes = JSON.parse(superHeroesText); // convert it to an object
  populateHeader(superHeroes);
}
//通过DOM Level 2 Events 函数 addEventListener()关联事件处理器
  xhr.addEventListener('loadend', () => {
    log.textContent = `${log.textContent}完成!状态码:${xhr.status}`;
  });


  1. JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
//JSON 格式创建一个对象
   var oo = {
        name:"hello",
        age:123,
        getName:function(){
       return oo.name;
            }
       
    }

  1. JSON 和 JS 对象互转: 利用浏览器内建的 JSON进行转换数据
//parse(): 以文本字符串形式接受 JSON 对象作为参数,并返回相应的对象。
var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'}
//stringify(): 接收一个对象作为参数,返回一个对应的 JSON 字符串。
var json = JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b": "World"}'

  1. 深拷贝多级对象
//深拷贝多级对象
const cloneObj = JSON.parse(JSON.stringify(obj));//JSON.stringify 对象的时候,包含 function, undefined or NaN 值的属性会从对象中移除。
//Shorthand for single level object
let obj = {x: 20, y: 'hello'};
const cloneObj = {...obj};
//————————————————
//版权声明:本文为CSDN博主「iOS逆向」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https://blog.csdn.net/z929118967/article/details/126142071

II Ajax(Asynchronous JavaScript and XML)

Ajax是一种在2005年由Google推广开来的编程模式,可以创建更好、更快以及更友好的web应用程序。

在这里插入图片描述Asynchronous JavaScript and XML基于javascript和HTTP请求,以异步地方式实现局部HTML的刷新。

2.1 从服务器获取数据

  1. XMLHttpRequest (通常缩写为 XHR)现在是一个相当古老的技术 - 它是在 20 世纪 90 年代后期由微软发明的,并且已经在相当长的时间内跨浏览器进行了标准化。

因为微软 Edge 浏览器的受宠, IE 已经没人管了

let request = new XMLHttpRequest();
request.open('GET', url);
request.responseType = 'text';

request.onload = function() {
  poemDisplay.textContent = request.response;
};

request.send();

  1. Fetch API 基本上是 XHR 的一个现代替代品——它是最近在浏览器中引入的,它使异步 HTTP 请求在 JavaScript 中更容易实现
fetch(url).then(function(response) {
  response.text().then(function(text) {
    poemDisplay.textContent = text;
  });
});

2.2 缓存问题

如果是get方式请求,浏览器会将请求的数据缓存起来,如果下次访问的地址没变,浏览器就不会发送真正的请求,会将缓存的数据显示给用户。

解决方式:

  1. 可以在地址后面加上一个随机数。
  2. 使用post方式发送请求。
❌
❌