frida-screenshot 3.0.3 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +29 -0
- package/dist/lib/ios.d.ts +2 -0
- package/dist/lib/ios.js +58 -0
- package/package.json +15 -5
- package/index.js +0 -30
- package/lib/ios.js +0 -66
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frida-screenshot
|
|
2
2
|
|
|
3
|
-
Grab screenshots using [Frida](https://
|
|
3
|
+
Grab screenshots using [Frida](https://frida.re).
|
|
4
4
|
|
|
5
5
|
## Example
|
|
6
6
|
|
|
7
7
|
```js
|
|
8
|
-
|
|
8
|
+
import screenshot from 'frida-screenshot';
|
|
9
9
|
|
|
10
10
|
const png = await screenshot();
|
|
11
11
|
send({
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ios } from './lib/ios.js';
|
|
2
|
+
const IOS = Symbol('ios');
|
|
3
|
+
const UNKNOWN = Symbol('unknown');
|
|
4
|
+
export default function screenshot(view) {
|
|
5
|
+
if (getOS() === IOS) {
|
|
6
|
+
return ios(view);
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
return new Promise(function (_, reject) {
|
|
10
|
+
reject(new Error('Not yet implemented for this OS'));
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
;
|
|
15
|
+
let cachedOS = null;
|
|
16
|
+
function getOS() {
|
|
17
|
+
if (cachedOS === null) {
|
|
18
|
+
cachedOS = detectOS();
|
|
19
|
+
}
|
|
20
|
+
return cachedOS;
|
|
21
|
+
}
|
|
22
|
+
function detectOS() {
|
|
23
|
+
if (ObjC.available && 'UIView' in ObjC.classes) {
|
|
24
|
+
return IOS;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return UNKNOWN;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/lib/ios.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const CGFloat = (Process.pointerSize === 4) ? 'float' : 'double';
|
|
2
|
+
const CGSize = [CGFloat, CGFloat];
|
|
3
|
+
export function ios(view) {
|
|
4
|
+
return performOnMainThread(() => {
|
|
5
|
+
const api = getApi();
|
|
6
|
+
if (api === null) {
|
|
7
|
+
throw new Error("Cannot retrieve the API");
|
|
8
|
+
}
|
|
9
|
+
if (!view) {
|
|
10
|
+
view = api.UIWindow.keyWindow();
|
|
11
|
+
}
|
|
12
|
+
const bounds = view.bounds();
|
|
13
|
+
const size = bounds[1];
|
|
14
|
+
api.UIGraphicsBeginImageContextWithOptions(size, 0, 0);
|
|
15
|
+
view.drawViewHierarchyInRect_afterScreenUpdates_(bounds, true);
|
|
16
|
+
const image = api.UIGraphicsGetImageFromCurrentImageContext();
|
|
17
|
+
api.UIGraphicsEndImageContext();
|
|
18
|
+
const png = new ObjC.Object(api.UIImagePNGRepresentation(image));
|
|
19
|
+
const data = png.bytes();
|
|
20
|
+
return data.readByteArray(png.length());
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
;
|
|
24
|
+
function performOnMainThread(action) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const api = getApi();
|
|
27
|
+
if (api.NSThread.isMainThread()) {
|
|
28
|
+
performAction();
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
ObjC.schedule(ObjC.mainQueue, performAction);
|
|
32
|
+
}
|
|
33
|
+
function performAction() {
|
|
34
|
+
try {
|
|
35
|
+
const result = action();
|
|
36
|
+
resolve(result);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
reject(e);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
;
|
|
45
|
+
let cachedApi = null;
|
|
46
|
+
function getApi() {
|
|
47
|
+
if (cachedApi === null) {
|
|
48
|
+
cachedApi = {
|
|
49
|
+
UIWindow: ObjC.classes.UIWindow,
|
|
50
|
+
NSThread: ObjC.classes.NSThread,
|
|
51
|
+
UIGraphicsBeginImageContextWithOptions: new NativeFunction(Module.getExportByName('UIKit', 'UIGraphicsBeginImageContextWithOptions'), 'void', [CGSize, 'bool', CGFloat]),
|
|
52
|
+
UIGraphicsEndImageContext: new NativeFunction(Module.getExportByName('UIKit', 'UIGraphicsEndImageContext'), 'void', []),
|
|
53
|
+
UIGraphicsGetImageFromCurrentImageContext: new NativeFunction(Module.getExportByName('UIKit', 'UIGraphicsGetImageFromCurrentImageContext'), 'pointer', []),
|
|
54
|
+
UIImagePNGRepresentation: new NativeFunction(Module.getExportByName('UIKit', 'UIImagePNGRepresentation'), 'pointer', ['pointer'])
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return cachedApi;
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frida-screenshot",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Grab screenshots using Frida",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"files": [
|
|
7
|
-
"/
|
|
8
|
-
"/lib/**/*.js"
|
|
8
|
+
"/dist/"
|
|
9
9
|
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"prepare": "npm run build",
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"watch": "tsc -w"
|
|
14
|
+
},
|
|
10
15
|
"repository": {
|
|
11
16
|
"type": "git",
|
|
12
17
|
"url": "git+https://github.com/nowsecure/frida-screenshot.git"
|
|
@@ -15,5 +20,10 @@
|
|
|
15
20
|
"bugs": {
|
|
16
21
|
"url": "https://github.com/nowsecure/frida-screenshot/issues"
|
|
17
22
|
},
|
|
18
|
-
"homepage": "https://github.com/nowsecure/frida-screenshot#readme"
|
|
23
|
+
"homepage": "https://github.com/nowsecure/frida-screenshot#readme",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/frida-gum": "^18.3.0",
|
|
26
|
+
"@types/node": "^18.11.18",
|
|
27
|
+
"typescript": "^4.7.3"
|
|
28
|
+
}
|
|
19
29
|
}
|
package/index.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
const ios = require('./lib/ios');
|
|
2
|
-
|
|
3
|
-
const IOS = Symbol('ios');
|
|
4
|
-
const UNKNOWN = Symbol('unknown');
|
|
5
|
-
|
|
6
|
-
module.exports = function (view) {
|
|
7
|
-
if (getOS() === IOS) {
|
|
8
|
-
return ios(view);
|
|
9
|
-
} else {
|
|
10
|
-
return new Promise(function (resolve, reject) {
|
|
11
|
-
reject(new Error('Not yet implemented for this OS'));
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
let cachedOS = null;
|
|
17
|
-
function getOS() {
|
|
18
|
-
if (cachedOS === null) {
|
|
19
|
-
cachedOS = detectOS();
|
|
20
|
-
}
|
|
21
|
-
return cachedOS;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function detectOS() {
|
|
25
|
-
if (ObjC.available && 'UIView' in ObjC.classes) {
|
|
26
|
-
return IOS;
|
|
27
|
-
} else {
|
|
28
|
-
return UNKNOWN;
|
|
29
|
-
}
|
|
30
|
-
}
|
package/lib/ios.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const CGFloat = (Process.pointerSize === 4) ? 'float' : 'double';
|
|
2
|
-
const CGSize = [CGFloat, CGFloat];
|
|
3
|
-
|
|
4
|
-
module.exports = function (view) {
|
|
5
|
-
return performOnMainThread(function () {
|
|
6
|
-
const api = getApi();
|
|
7
|
-
|
|
8
|
-
if (!view) {
|
|
9
|
-
view = api.UIWindow.keyWindow();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const bounds = view.bounds();
|
|
13
|
-
const size = bounds[1];
|
|
14
|
-
api.UIGraphicsBeginImageContextWithOptions(size, 0, 0);
|
|
15
|
-
|
|
16
|
-
view.drawViewHierarchyInRect_afterScreenUpdates_(bounds, true);
|
|
17
|
-
|
|
18
|
-
const image = api.UIGraphicsGetImageFromCurrentImageContext();
|
|
19
|
-
api.UIGraphicsEndImageContext();
|
|
20
|
-
|
|
21
|
-
const png = new ObjC.Object(api.UIImagePNGRepresentation(image));
|
|
22
|
-
return png.bytes().readByteArray(png.length());
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function performOnMainThread(action) {
|
|
27
|
-
return new Promise(function (resolve, reject) {
|
|
28
|
-
if (getApi().NSThread.isMainThread()) {
|
|
29
|
-
performAction();
|
|
30
|
-
} else {
|
|
31
|
-
ObjC.schedule(ObjC.mainQueue, performAction);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function performAction() {
|
|
35
|
-
try {
|
|
36
|
-
const result = action();
|
|
37
|
-
resolve(result);
|
|
38
|
-
} catch (e) {
|
|
39
|
-
reject(e);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let cachedApi = null;
|
|
46
|
-
function getApi() {
|
|
47
|
-
if (cachedApi === null) {
|
|
48
|
-
cachedApi = {
|
|
49
|
-
UIWindow: ObjC.classes.UIWindow,
|
|
50
|
-
NSThread: ObjC.classes.NSThread,
|
|
51
|
-
UIGraphicsBeginImageContextWithOptions: new NativeFunction(
|
|
52
|
-
Module.findExportByName('UIKit', 'UIGraphicsBeginImageContextWithOptions'),
|
|
53
|
-
'void', [CGSize, 'bool', CGFloat]),
|
|
54
|
-
UIGraphicsEndImageContext: new NativeFunction(
|
|
55
|
-
Module.findExportByName('UIKit', 'UIGraphicsEndImageContext'),
|
|
56
|
-
'void', []),
|
|
57
|
-
UIGraphicsGetImageFromCurrentImageContext: new NativeFunction(
|
|
58
|
-
Module.findExportByName('UIKit', 'UIGraphicsGetImageFromCurrentImageContext'),
|
|
59
|
-
'pointer', []),
|
|
60
|
-
UIImagePNGRepresentation: new NativeFunction(
|
|
61
|
-
Module.findExportByName('UIKit', 'UIImagePNGRepresentation'),
|
|
62
|
-
'pointer', ['pointer'])
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
return cachedApi;
|
|
66
|
-
}
|