函数如何通过 [[FunctionLocation]] 到达外部脚本?
How can a function reach an external script with [[FunctionLocation]]?
我有一些代码使用 google api 在屏幕上显示共享对话框。 (这个:https://developers.google.com/drive/api/v3/share-button)
share_client = new gapi.drive.share.ShareClient()
share_client.showSettingsDialog()
但是在调用 showSettingsDialog
之后,它失败并显示 403。我可以在我的网络选项卡中看到该请求:
我知道它为什么会失败,我相信只要我能为这个请求添加一个额外的参数,我就能解决这个问题。问题是我不能使用像 Service Worker 这样的东西来拦截和更改请求,因为这个请求实际上是从 apis.google.com 的脚本发起的,而不是从我的页面位置发起的,所以我没有访问权限与 Service Worker 一起使用。如果可能的话,也很难修改此 api 代码以发出我想要的请求。我已经尝试了很多。
如果我检查那个 share_client
对象,我可以看到它从我控制的代码转移到外部资源的地方。这是因为被调用的函数有这个[[FunctionLocation]]
,它指向:https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi. ... /cb=gapi.loaded_2
。如果我进入调试器,我可以看到它从这个函数调用跳转到 API 的本地副本,再跳转到 apis.google.com
中托管的外部脚本
所以我的问题是这究竟是如何工作的?函数如何指向外部脚本?这个 [[FunctionLocation]]
是从哪里来的?它是如何产生的?有可能改变吗?它如何在不实际发出 http 请求的情况下到达此外部代码?
基本上,我是在询问有关这一切如何组合在一起的任何见解。我不太确定是否真的有可能像我想的那样向该请求添加一个参数,但在这一点上,我主要只是想了解更多相关信息。
在进行逆向工程时,我们可以在 https://apis.google.com/js/api.js
JavaScript 代码中看到:
var W = decodeURI("%73cript");
相当于:
var W = "script";
原因可能是为了提供某种混淆以防止我们对代码进行逆向工程,但这还不足以阻止我们!
那么,就是这个函数:
va = function (a) {
var b = v.createElement(W);
b.setAttribute("src", Y ? Y.createScriptURL(a) : a);
a = ua();
null !== a && b.setAttribute("nonce", a);
b.async = "true";
(a = v.getElementsByTagName(W)[0]) ? a.parentNode.insertBefore(b, a) : (v.head || v.body || v.documentElement).appendChild(b)
}
这是一种非常常用的动态添加脚本到页面的方法(使用给定的 src
创建一个 script
元素,然后将其添加到 DOM)。
这是 https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.fr.VPdiypayOnU.O/m=drive_share/rt=j/sv=1/d=1/ed=1/am=wQc/rs=AGLTcCPUjzWofrE8wWhx40v1IW1jnz4lrQ/cb=gapi.loaded_0
脚本的加载方式。
在这个脚本的最后,我们可以看到:
lU.prototype.E2 = function() {
this.Ds.Gh("settings")
}
;
_.z("gapi.drive.share.ShareClient", lU);
lU.prototype.setItemIds = lU.prototype.HE;
lU.prototype.setOAuthToken = lU.prototype.KE;
lU.prototype.showSettingsDialog = lU.prototype.E2;
这是 showSettingsDialog
分配给 ShareClient.prototype
的地方。您可以轻松地将它替换为您自己的函数,但这对您没有帮助,因为您要修改的函数是其他函数(由 this.Ds.Gh("settings")
间接调用)。
不幸的是,我强烈怀疑您是否可以修改该底层函数,因为所有内容都包含在回调中:gapi.loaded_0(function(_) {...
,并且由于此代码,此回调外部唯一可用的是 ShareClient
:
_.q = this || self; // contains window during execution
_.z = function(a, b) {
a = a.split(".");
var c = _.q;
a[0]in c || "undefined" == typeof c.execScript || c.execScript("var " + a[0]);
for (var d; a.length && (d = a.shift()); )
a.length || void 0 === b ? c = c[d] && c[d] !== Object.prototype[d] ? c[d] : c[d] = {} : c[d] = b
}
这段代码就像一个 polyfill,它改变了全局 gapi
对象并将 ShareClient
分配给 gapi.drive.share
,其他所有内容都在匿名回调中对外界隐藏, 防止你修改它。
但是,没有什么能阻止您使用 Service Worker 作为此请求的代理。 Service Worker 将处理任何发起请求以及目标域是什么。唯一不能使用 Service Worker 的情况是拦截 iframe
请求,因为 iframe
被视为单独的页面,但在您的情况下有 none (请求由加载到当前页面的 script
发起。
我有一些代码使用 google api 在屏幕上显示共享对话框。 (这个:https://developers.google.com/drive/api/v3/share-button)
share_client = new gapi.drive.share.ShareClient()
share_client.showSettingsDialog()
但是在调用 showSettingsDialog
之后,它失败并显示 403。我可以在我的网络选项卡中看到该请求:
我知道它为什么会失败,我相信只要我能为这个请求添加一个额外的参数,我就能解决这个问题。问题是我不能使用像 Service Worker 这样的东西来拦截和更改请求,因为这个请求实际上是从 apis.google.com 的脚本发起的,而不是从我的页面位置发起的,所以我没有访问权限与 Service Worker 一起使用。如果可能的话,也很难修改此 api 代码以发出我想要的请求。我已经尝试了很多。
如果我检查那个 share_client
对象,我可以看到它从我控制的代码转移到外部资源的地方。这是因为被调用的函数有这个[[FunctionLocation]]
,它指向:https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi. ... /cb=gapi.loaded_2
。如果我进入调试器,我可以看到它从这个函数调用跳转到 API 的本地副本,再跳转到 apis.google.com
所以我的问题是这究竟是如何工作的?函数如何指向外部脚本?这个 [[FunctionLocation]]
是从哪里来的?它是如何产生的?有可能改变吗?它如何在不实际发出 http 请求的情况下到达此外部代码?
基本上,我是在询问有关这一切如何组合在一起的任何见解。我不太确定是否真的有可能像我想的那样向该请求添加一个参数,但在这一点上,我主要只是想了解更多相关信息。
在进行逆向工程时,我们可以在 https://apis.google.com/js/api.js
JavaScript 代码中看到:
var W = decodeURI("%73cript");
相当于:
var W = "script";
原因可能是为了提供某种混淆以防止我们对代码进行逆向工程,但这还不足以阻止我们!
那么,就是这个函数:
va = function (a) {
var b = v.createElement(W);
b.setAttribute("src", Y ? Y.createScriptURL(a) : a);
a = ua();
null !== a && b.setAttribute("nonce", a);
b.async = "true";
(a = v.getElementsByTagName(W)[0]) ? a.parentNode.insertBefore(b, a) : (v.head || v.body || v.documentElement).appendChild(b)
}
这是一种非常常用的动态添加脚本到页面的方法(使用给定的 src
创建一个 script
元素,然后将其添加到 DOM)。
这是 https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.fr.VPdiypayOnU.O/m=drive_share/rt=j/sv=1/d=1/ed=1/am=wQc/rs=AGLTcCPUjzWofrE8wWhx40v1IW1jnz4lrQ/cb=gapi.loaded_0
脚本的加载方式。
在这个脚本的最后,我们可以看到:
lU.prototype.E2 = function() {
this.Ds.Gh("settings")
}
;
_.z("gapi.drive.share.ShareClient", lU);
lU.prototype.setItemIds = lU.prototype.HE;
lU.prototype.setOAuthToken = lU.prototype.KE;
lU.prototype.showSettingsDialog = lU.prototype.E2;
这是 showSettingsDialog
分配给 ShareClient.prototype
的地方。您可以轻松地将它替换为您自己的函数,但这对您没有帮助,因为您要修改的函数是其他函数(由 this.Ds.Gh("settings")
间接调用)。
不幸的是,我强烈怀疑您是否可以修改该底层函数,因为所有内容都包含在回调中:gapi.loaded_0(function(_) {...
,并且由于此代码,此回调外部唯一可用的是 ShareClient
:
_.q = this || self; // contains window during execution
_.z = function(a, b) {
a = a.split(".");
var c = _.q;
a[0]in c || "undefined" == typeof c.execScript || c.execScript("var " + a[0]);
for (var d; a.length && (d = a.shift()); )
a.length || void 0 === b ? c = c[d] && c[d] !== Object.prototype[d] ? c[d] : c[d] = {} : c[d] = b
}
这段代码就像一个 polyfill,它改变了全局 gapi
对象并将 ShareClient
分配给 gapi.drive.share
,其他所有内容都在匿名回调中对外界隐藏, 防止你修改它。
但是,没有什么能阻止您使用 Service Worker 作为此请求的代理。 Service Worker 将处理任何发起请求以及目标域是什么。唯一不能使用 Service Worker 的情况是拦截 iframe
请求,因为 iframe
被视为单独的页面,但在您的情况下有 none (请求由加载到当前页面的 script
发起。