[add]添加cordova完整性检验
This commit is contained in:
commit
0ea70324cb
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2016 Davide Doronzo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
||||
Cescit-Integrity Cordova Plugin
|
||||
=============================
|
||||
|
||||
本插件基于cordova-plugin-antitampering,有apk完整性检验,res资源检验,assert资源检验的功能。
|
||||
|
||||
Supports Android
|
26
package.json
Normal file
26
package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "cordova-plugin-cescit-integrity",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "lwj"
|
||||
},
|
||||
"description": "本插件基于cordova-plugin-antitampering,有apk完整性检验,res资源检验,assert资源检验的功能。",
|
||||
"cordova": {
|
||||
"id": "cordova-plugin-cescit-integrity",
|
||||
"platforms": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/CESCIT/cordova-plugin-cescit-integrity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"ecosystem:cordova",
|
||||
"cordova-android"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"license": "ISC"
|
||||
}
|
47
plugin.xml
Normal file
47
plugin.xml
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
id="cordova-plugin-cescit-integrity"
|
||||
version="1.0.0">
|
||||
<name>cescit-integrity</name>
|
||||
<description>基于cordova-plugin-antitampering的文件完整性检验 - Android</description>
|
||||
<author>lwj</author>
|
||||
<license>MIT</license>
|
||||
|
||||
<engines>
|
||||
<engine name="cordova" version=">=5.4" />
|
||||
</engines>
|
||||
|
||||
<js-module src="www/AntiTampering.js" name="AntiTampering">
|
||||
<clobbers target="cordova.plugins.cescitIntegrity" />
|
||||
</js-module>
|
||||
|
||||
<!-- <hook type="after_prepare" src="scripts/clear_hashes.js" /> -->
|
||||
<!-- <hook type="before_run" src="scripts/clear_hashes.js" /> -->
|
||||
<hook type="before_build" src="scripts/clear_hashes.js" />
|
||||
|
||||
<hook type="before_run" src="scripts/save_assets_hashes.js" />
|
||||
<hook type="before_run" src="scripts/save_res_hashes.js" />
|
||||
<hook type="before_build" src="scripts/save_assets_hashes.js" />
|
||||
<hook type="before_build" src="scripts/save_res_hashes.js" />
|
||||
|
||||
<platform name="android">
|
||||
<config-file target="res/xml/config.xml" parent="/*">
|
||||
<feature name="CescitIntegrity">
|
||||
<param name="android-package" value="com.cescit.integrity.CescitIntegrity"/>
|
||||
<param name="onload" value="true" />
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/android/com/cescit/integrity/Config.java" target-dir="src/com/cescit/integrity" />
|
||||
<source-file src="src/android/com/cescit/integrity/CescitIntegrity.java" target-dir="src/com/cescit/integrity" />
|
||||
<source-file src="src/android/com/cescit/integrity/AssetsIntegrity.java" target-dir="src/com/cescit/integrity" />
|
||||
<source-file src="src/android/com/cescit/integrity/ResIntegrity.java" target-dir="src/com/cescit/integrity" />
|
||||
<source-file src="src/android/com/cescit/integrity/ApkIntegrity.java" target-dir="src/com/cescit/integrity" />
|
||||
<source-file src="src/android/com/cescit/integrity/DebugDetection.java" target-dir="src/com/cescit/integrity" />
|
||||
<source-file src="src/android/com/cescit/integrity/TamperingException.java" target-dir="src/com/cescit/integrity" />
|
||||
<source-file src="src/android/com/cescit/integrity/HttpUtil.java" target-dir="src/com/cescit/integrity" />
|
||||
|
||||
</platform>
|
||||
|
||||
</plugin>
|
30
scripts/clear_hashes.js
Normal file
30
scripts/clear_hashes.js
Normal file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var helpers = require('./helpers');
|
||||
|
||||
module.exports = function (context) {
|
||||
var fs = require('fs');
|
||||
var fileList = ['AssetsIntegrity', 'ResIntegrity', 'ApkIntegrity'];
|
||||
process.stdout.write('[完整性检验] Clearing assets hash from previous build\n');
|
||||
|
||||
helpers.getPlatformsList(context).forEach(function (platform) {
|
||||
fileList.forEach(fileName => {
|
||||
var source = helpers.getFileMapContent(context, platform, fileName);
|
||||
var content = source.content;
|
||||
|
||||
let regexp = ''
|
||||
if(fileName == 'AssetsIntegrity') regexp = /hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/
|
||||
else if(fileName == 'ResIntegrity') regexp = /hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/
|
||||
else if(fileName == 'ApkIntegrity') regexp = /hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/
|
||||
content = source.content.replace(/\s*put\("[^"]+",\s"[^"]{64}"\);/g, '')
|
||||
.replace(regexp, function (match, group) {
|
||||
return match.replace(group, '()\n ');
|
||||
});
|
||||
try {
|
||||
fs.writeFileSync(source.path, content, 'utf-8');
|
||||
} catch (e) {
|
||||
helpers.exit('Unable to write java class source at path ' + source.path, e);
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
4
scripts/helpers/error_exit.js
Normal file
4
scripts/helpers/error_exit.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = function (msg, exception) {
|
||||
process.stdout.write('\n[完整性检验] ERROR! ' + msg + '\n');
|
||||
throw new Error(exception);
|
||||
};
|
32
scripts/helpers/getFileMapContent.js
Normal file
32
scripts/helpers/getFileMapContent.js
Normal file
@ -0,0 +1,32 @@
|
||||
var helpers = require('./');
|
||||
|
||||
module.exports = function (platform, fileName) {
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var cordovaUtil = this.requireCordovaModule('cordova-lib/src/cordova/util');
|
||||
var projectRoot = cordovaUtil.isCordova();
|
||||
var platformPath = path.join(projectRoot, 'platforms', platform);
|
||||
var sourceFile;
|
||||
var content;
|
||||
|
||||
if (platform === 'android') {
|
||||
var fileBasename = fileName;
|
||||
var filePath = 'com/cescit/integrity/' + fileBasename + '.java';
|
||||
try {
|
||||
sourceFile = path.join(platformPath, 'app/src/main/java', filePath);
|
||||
content = fs.readFileSync(sourceFile, 'utf-8');
|
||||
} catch (_e) {
|
||||
try {
|
||||
sourceFile = path.join(platformPath, 'src', filePath);
|
||||
content = fs.readFileSync(sourceFile, 'utf-8');
|
||||
} catch (e) {
|
||||
helpers.exit('Unable to read java class source at path ' + sourceFile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: content,
|
||||
path: sourceFile
|
||||
};
|
||||
};
|
5
scripts/helpers/get_platforms_list.js
Normal file
5
scripts/helpers/get_platforms_list.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = function () {
|
||||
return (this.opts.platforms || this.opts.cordova.platforms || []).filter(function (platform) {
|
||||
return this.opts.plugin.pluginInfo.getPlatformsArray().indexOf(platform) > -1;
|
||||
}, this);
|
||||
};
|
26
scripts/helpers/index.js
Normal file
26
scripts/helpers/index.js
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Exit script with custom error log.
|
||||
* @param {string} msg - Error message.
|
||||
* @param {Error} exception
|
||||
*/
|
||||
exports.exit = require('./error_exit');
|
||||
|
||||
/**
|
||||
* Get the real list of platforms affected by a running plugin hook.
|
||||
* @param {Object} context - Cordova context.
|
||||
*/
|
||||
exports.getPlatformsList = invokeHelper.bind(null, './get_platforms_list');
|
||||
|
||||
/**
|
||||
* Detect if the context process is running with verbose option.
|
||||
* @param {Object} context - Cordova context.
|
||||
*/
|
||||
exports.isVerbose = invokeHelper.bind(null, './is_verbose');
|
||||
|
||||
exports.getFileMapContent = invokeHelper.bind(null, './getFileMapContent');
|
||||
|
||||
function invokeHelper (path) {
|
||||
var helper = require(path);
|
||||
var context = arguments[1];
|
||||
return helper.apply(context, Array.prototype.splice.call(arguments, 2));
|
||||
}
|
4
scripts/helpers/is_verbose.js
Normal file
4
scripts/helpers/is_verbose.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = function () {
|
||||
return this.opts && this.opts.options && this.opts.options.verbose ||
|
||||
typeof this.cmdLine === 'string' && this.cmdLine.indexOf(' -verbose') > -1;
|
||||
};
|
111
scripts/save_assets_hashes.js
Normal file
111
scripts/save_assets_hashes.js
Normal file
@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var crypto = require('crypto');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
module.exports = function (context) {
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var cordovaUtil = context.requireCordovaModule('cordova-lib/src/cordova/util');
|
||||
var platforms = context.requireCordovaModule('cordova-lib/src/platforms/platforms');
|
||||
var projectRoot = cordovaUtil.isCordova();
|
||||
|
||||
process.stdout.write('[完整性检验] Saving a hash for each platforms asset \n');
|
||||
|
||||
function getPlatformAssets (dir) {
|
||||
var assetsList = [];
|
||||
var list = fs.readdirSync(dir);
|
||||
list.map(function (file) {
|
||||
var filePath = path.join(dir, file);
|
||||
if (fs.statSync(filePath).isDirectory()) {
|
||||
var subDirList = getPlatformAssets(filePath);
|
||||
assetsList = assetsList.concat(subDirList);
|
||||
}
|
||||
if (fs.statSync(filePath).isFile()) {
|
||||
assetsList.push(filePath);
|
||||
}
|
||||
});
|
||||
return assetsList;
|
||||
}
|
||||
|
||||
helpers.getPlatformsList(context).forEach(function (platform) {
|
||||
var platformPath = path.join(projectRoot, 'platforms', platform);
|
||||
var platformApi = platforms.getPlatformApi(platform, platformPath);
|
||||
var platformInfo = platformApi.getPlatformInfo();
|
||||
var platformWww = platformInfo.locations.www;
|
||||
var platformAssets = path.resolve(platformWww, '../') // assets文件夹
|
||||
var source = helpers.getFileMapContent(context, platform, 'AssetsIntegrity');
|
||||
var content = source.content;
|
||||
|
||||
var hashes = getPlatformAssets(platformAssets).map(function (file) {
|
||||
var fileName = file.replace(/\\/g, '/');
|
||||
fileName = fileName.replace(platformAssets.replace(/\\/g, '/') + '/', '');
|
||||
var hash;
|
||||
var hashHex;
|
||||
hash = crypto.createHash('sha256');
|
||||
try {
|
||||
hash.update(fs.readFileSync(file), 'utf8');
|
||||
} catch (e) {
|
||||
helpers.exit('Unable to read file at path ' + file, e);
|
||||
}
|
||||
hashHex = hash.digest('hex');
|
||||
if (helpers.isVerbose(context)) {
|
||||
process.stdout.write('Hash: ' + hashHex + ' < ' + fileName + '\n');
|
||||
}
|
||||
return {
|
||||
// file: Buffer.from(fileName).toString('base64'),
|
||||
file: fileName,
|
||||
hash: hashHex
|
||||
};
|
||||
});
|
||||
|
||||
if (platform === 'android') {
|
||||
content = content.replace(/\s*put\("[^"]+",\s"[^"]{64}"\);/g, '')
|
||||
.replace(/hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/, function (match, group) {
|
||||
return match.replace(group, '()\n' + tab());
|
||||
})
|
||||
.replace(/hashList\s*=.+\s*new.*(\(.*\))/, function (match, group) {
|
||||
var replace = match.replace(group, '(' + (hashes.length || '') + ')');
|
||||
if (hashes.length) {
|
||||
replace += ' {{\n' + tab();
|
||||
hashes.forEach(function (h) {
|
||||
replace += tab(2) + 'put("' + h.file + '", "' + h.hash + '");\n' + tab();
|
||||
});
|
||||
replace += tab() + '}}';
|
||||
}
|
||||
return replace;
|
||||
});
|
||||
|
||||
try {
|
||||
fs.writeFileSync(source.path, content, 'utf-8');
|
||||
} catch (e) {
|
||||
helpers.exit('Unable to write java class source at path ' + source.path, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (platform === 'ios') {
|
||||
content = content.replace(/hashList = (@{([^}]*)});/, function (a, b) {
|
||||
var list = '@{\n' + tab();
|
||||
hashes.forEach(function (h) {
|
||||
list += tab() + '@"' + h.file + '": @"' + h.hash + '",\n' + tab();
|
||||
});
|
||||
list += '}';
|
||||
return a.replace(b, list);
|
||||
});
|
||||
|
||||
try {
|
||||
fs.writeFileSync(source.path, content, 'utf-8');
|
||||
} catch (e) {
|
||||
helpers.exit('Unable to write obj-c source at path ' + source.path, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function tab (size) {
|
||||
var str = '';
|
||||
for (var i = 0; i < (size || 1); i++) {
|
||||
str += ' ';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
};
|
415
scripts/save_res_hashes.js
Normal file
415
scripts/save_res_hashes.js
Normal file
@ -0,0 +1,415 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var crypto = require('crypto');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
module.exports = function (context) {
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var cordovaUtil = context.requireCordovaModule('cordova-lib/src/cordova/util');
|
||||
var platforms = context.requireCordovaModule('cordova-lib/src/platforms/platforms');
|
||||
var projectRoot = cordovaUtil.isCordova();
|
||||
|
||||
process.stdout.write('[完整性检验] Saving a hash for each platforms res \n');
|
||||
|
||||
function getHashList() {
|
||||
let str = `
|
||||
put("res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "d9cf644f86abcabfb29d7020e1f9bf51daf851ecb5b652eb60e668654cc68cd1")
|
||||
put("res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "6948c22791982b06d1d911fd6212ee29bf8a819196ec01f60f5ddcf852f5394f")
|
||||
put("res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png", "d5c644b00ee79bd4f1fcb789ebbf89e79c0c6fea878dfbc4a8c64c3be34b5b03")
|
||||
put("res/drawable-mdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "6111c327f535aa76f4ceaa6b1f91c23252c3936f86e52100795594ae00e73b0f")
|
||||
put("res/layout-v17/select_dialog_singlechoice_material.xml", "663a381007b438d9eeaa4f535323bc5a975e23bb2df0207b359545cb493d5347")
|
||||
put("res/drawable-xxxhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "dfe5008780f6427172ccb49cd0c2f5ccd0aaa3be3921b875362c9e65a08df76c")
|
||||
put("res/drawable-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "500419896d42ee0818f210db317439df7458ad9f6f68f8b33690bd6f18c7d05a")
|
||||
put("res/drawable-xxhdpi-v4/abc_list_divider_mtrl_alpha.9.png", "49903073ab23abfc11a55e1333eb2905c743e1b382006c27c15434fd76e0207c")
|
||||
put("res/layout/zxing_capture.xml", "91c24841125766e60e238c2cc84eb50e98fd2accbdaa6bcfec4331e0f50cbf64")
|
||||
put("res/drawable-ldrtl-xxxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "21b9bc9ca5bf09b7390dc9848395f81aca27dc6a44dd71f44a4718ee2698176a")
|
||||
put("res/drawable-xxhdpi-v4/ic_action_remove.png", "a3bc9705eab4c61770b493dab577eb1cfc6d96e70c735a6cfd6da9b44028e728")
|
||||
put("res/drawable-xxhdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "178c653f6fdf2201b3eb6575839af350499f5c357a0fc2b7969a99824adf88f2")
|
||||
put("res/drawable-xhdpi-v4/abc_switch_track_mtrl_alpha.9.png", "8154b2e598f4cd681133db7634f89194ef4c7dd6d0f48eb48480f93234848400")
|
||||
put("res/drawable-xxxhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "323f7fbb7907c8df09c96bb41ae71ebc0b106a046ce4461510ad8f6424ed44a6")
|
||||
put("res/layout/abc_action_bar_view_list_nav_layout.xml", "37d81d4b028ad5194a93fdbd2cb6f1b0e5b5317a2415e6d6534f7644a19a3a9b")
|
||||
put("res/drawable/abc_list_selector_background_transition_holo_light.xml", "3553ef7b4d6984143bc4ee5bfd2556c1627cac4301c1800a1b7ae012c34905bc")
|
||||
put("res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "540d0e23cd0c32a128c17bcee2982f1a47ef20ed1bf27fd0ee68db8ce1fabee4")
|
||||
put("res/drawable-xxhdpi-v4/ic_action_next_item.png", "058f15a4afa192620e0bc8e4a7268f494a89495722337f889ed3109655a88d82")
|
||||
put("res/color/abc_primary_text_disable_only_material_dark.xml", "b0eaa0e755bc2bf1d8cafe537920e0ceb507ac43f037806cbd2c4b9f92162cfe")
|
||||
put("res/drawable-xxhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "b6e9825dcb2642d75a4ec0b4fbbec30c2fcd138c151290d3330682b588b3714f")
|
||||
put("res/anim/abc_slide_in_bottom.xml", "18a752f91c63b61542bd743fab11657ccb0e11df795410c9cbb7b23b94651335")
|
||||
put("res/drawable-mdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "089fd6dbea6ef15371447199ab8fef7f17d4acee7362f33692a565d4dafe1bdf")
|
||||
put("res/layout/notification_template_part_chronometer.xml", "d51d12dbdbfa1db0238a35ba91610e628f35f95ded74eca929d243976ce46b36")
|
||||
put("res/drawable-xhdpi-v4/abc_list_pressed_holo_light.9.png", "270ec2c94be001d62c967e5f5bebd180e24c48c10cfd7c108fd667378cfc95a9")
|
||||
put("res/layout/abc_alert_dialog_button_bar_material.xml", "31ff21c6b8e73e150630521ab3d39fdc8b802731ec7cd9fd8792e044c470bba4")
|
||||
put("res/drawable-ldrtl-mdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "40a015816f1754c099902ba322b2942d1f9fd91752615e160ad7dfd30a5abdec")
|
||||
put("res/drawable-mdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "dfd5ae1cebc27420abc8ed8a789c3645caa665c1ea2a11577cb6468cb173f895")
|
||||
put("res/drawable-land-mdpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "4eeb04eb02e6f07453e1d1972c5bea7be288c1100e508c4e106272360f8443d3")
|
||||
put("res/layout/abc_action_menu_item_layout.xml", "5788c0d59e649fe9bfa1cc6b5d054695472fdc7734ce9e69dc023d421dc464ca")
|
||||
put("res/drawable-xhdpi-v4/abc_list_selector_disabled_holo_light.9.png", "51d83e107f732b13b915e32b185c78e4ad70fc6d0c23daa200f5ad710e7d4525")
|
||||
put("res/drawable-xhdpi-v4/abc_list_pressed_holo_dark.9.png", "330791ec39b85952980ff0fa119f4360f6c68be646eae3fcc2e0129c485e017c")
|
||||
put("res/drawable-mdpi-v4/ic_action_previous_item.png", "903b76da45e94a166cba5adcf1b0a07d24af13eb7b21252a8c7a06c8d81a7c83")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_clear_mtrl_alpha.png", "a7edc1182f345588539e644543e6f8ab4c4aec3b5919bd29becb93147622eb11")
|
||||
put("res/drawable-mdpi-v4/abc_list_pressed_holo_dark.9.png", "50b3049da5275342546813eb61495d611bb82db21ef771846b9ca2b7d5c2d1fc")
|
||||
put("res/drawable-hdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "81fbbbc931441a4f6d64680a9d2f6c645c79209010a572f2ea0b732143039e73")
|
||||
put("res/drawable-xxhdpi-v4/abc_list_pressed_holo_dark.9.png", "805839b77513ec41b4bf4b2e5ee12ed06138d319621eed15bb049d31165ce08b")
|
||||
put("res/drawable/abc_list_selector_background_transition_holo_dark.xml", "2a67a427df005ed7b8d3aef7cdc131684ffa5d41d5a4734bffeeeaf1824f317c")
|
||||
put("res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "0d1f5a3e36d02311640190fd768ee8d2607beba7a90b411be77a48a36427cb77")
|
||||
put("res/drawable-xhdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "d19092254aa4133fba8b7795fb5db560ad5dcd16c6f79201bec31b0f1e9b608b")
|
||||
put("res/drawable-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "9be46560847b3596a1406bda8b7a8449477f3314206c9a19b53e6703653db4d7")
|
||||
put("res/xml/config.xml", "e2a12496c7a21deeb3a7b5f9b595ab7bc83dda00a4207e04f03f09126df6ce1d")
|
||||
put("res/layout/abc_search_dropdown_item_icons_2line.xml", "0853dd90f73c408322e623d532ae54fd3947618bb37f19d187efd568d2e0e571")
|
||||
put("res/drawable-hdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "7f537a60f30b3e56e0c091bae0747a3407fdcd9d648e6fa61ec6b44331a8bb0d")
|
||||
put("res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_015.png", "5ced2ab774853050255d7309e10b76217813d56f3f8ab8fb73ac3f635d90c676")
|
||||
put("res/drawable/abc_cab_background_top_material.xml", "69b62b2562a9ac8df2efefb63c00c24dfc975e76c0b46561973a10642189394c")
|
||||
put("res/layout-v17/abc_search_view.xml", "bfcc1746978149788fb7d4efbd4516464a7566583527b1edfdb0333b7fc7815c")
|
||||
put("res/color-v11/abc_background_cache_hint_selector_material_dark.xml", "261c978a306f7388c522ca0af7edc0700b33d5eb83e08b9b9ddc513ab5c24d90")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "b3ca8774b4123d57482664a575a513369c7bda589ffca5c0b10a88b800c52235")
|
||||
put("res/color/abc_primary_text_material_light.xml", "5859bc2fd64e2bba5f94a50a3bd586596bf3f6cd8945220d7e8c706c671484fa")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_go_search_api_mtrl_alpha.png", "c79caef928e8b856634a0ead1a31dad8f755ca02918ce3dbe26a2252e1300a96")
|
||||
put("res/drawable-xhdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "5c6a585c92347593e7344d8fd432487f9716fb6dec72ebd270ed9c8d5b597cb2")
|
||||
put("res/drawable-xhdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "4ba313b3b8c9e8dd9536dc180d7ba6147b6954fa69a64e3bf38fc5dac5f1863f")
|
||||
put("res/layout/abc_screen_simple_overlay_action_mode.xml", "3e8596db7c3f547bc578d0c246678282f8f1d92f3a328a72b52bac4ee1658cc5")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "e689b9c84076ff5c339e2a3c6914321458d5c603a0596564e623442cc783e76c")
|
||||
put("res/drawable-xhdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "dd8dbab387958637a6b7d78c8d6cd88da4370b82c6f2767562b959e1db954b8e")
|
||||
put("res/drawable-ldrtl-xxxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "753e4526bd3dd72cf154bb2dd6595e0bab1a002540aba793cb7f0e67dd8adfef")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "31e46a844a40519da6b9168b7a631cbddf0137671c0cd25485c68a60a1f02fcb")
|
||||
put("res/layout-v17/notification_template_big_media_narrow.xml", "1f037610ac5a05a48eb512aba265972637302c24706f2ea1e1b90d69280c1544")
|
||||
put("res/drawable-ldrtl-xxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "8b21794e8bcfb520ee54d2c8162521fc74087d091a24ea3d22ffd1031a111fdb")
|
||||
put("res/drawable-xxhdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "966103c0bb0723292dfbc415653363cd4af2d874b7762a5c197a9a5aa1997330")
|
||||
put("res/drawable-ldrtl-xxxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "3c287f4b4f894271811814a21408129444d0c79c51cd5729054d65c108b2098d")
|
||||
put("res/color/abc_primary_text_disable_only_material_light.xml", "d233aef93574787c825c4f41b796ca667ab2d58e8769f1dc01ac611fbdbb4585")
|
||||
put("res/drawable-xhdpi-v4/ic_action_remove.png", "548204063fbffed3d0458abad54a6a66eb09ea374eb6a9477c60bf199f70d281")
|
||||
put("res/anim/abc_fade_in.xml", "aac9596dcef2ac796b74f2c2b4c149e9a66b004e864b37e82270752c66b1234d")
|
||||
put("res/drawable-ldrtl-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "0df6d1fe57b9b28bac3a6a2b82941bcddab7e616a025201aacbe5e90c48c2068")
|
||||
put("res/drawable-land-ldpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "ed2b544497195069d55663bd91b5cbfa5c732b9d4d264ef201394d8ef59deb6e")
|
||||
put("res/drawable/abc_seekbar_thumb_material.xml", "23aa9a856885c822bee0f578d1a2f17fe4c67e08281223a4669850b6a5e588ef")
|
||||
put("res/anim/abc_fade_out.xml", "b13a8275d8ff8077b29b8413c6809ca3ff05fc16698ac2564ffc3b3325cf8302")
|
||||
put("res/anim/abc_popup_exit.xml", "54702462887e035398f7a686b146e0c0660951f57faef59b7d4dbce21231aa6a")
|
||||
put("res/drawable-hdpi-v4/ic_action_next_item.png", "8d802c3a172ebb13ea9fab6371cf2da9e03ce3cff2cd1fc924eec446a8f19cde")
|
||||
put("res/color/abc_search_url_text.xml", "01a8d0346e9e0e932fac0dbe2f9329589109651e208905efe700518f2d756858")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "b78d97845a150185ff66371398582deddeccc81c4562b7ef987c3fd6c0a7631e")
|
||||
put("res/drawable-xhdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "0f5bd2f01b9689db08e4de977078e856427b126674afa8ebe7f0a9ff80aceea2")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "6a42b5738417c10db0d053cdffbbd6667a94d614f449d47e6c435126da47c599")
|
||||
put("res/drawable-xhdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "4914c1f7b9f7185e0361aa4ea6bcc7b6e45a95d534b8f8299282ba45371734ed")
|
||||
put("res/drawable-hdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "e81784cdc8ac38373621e586d28d488d585ccf2d695759ed611d848d2bf04ca7")
|
||||
put("res/drawable-mdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "893e34d7adb3ad5d8ec6b443b0791ac1dd77b8e43496e43a1e2e1f6e5f0af975")
|
||||
put("res/drawable-xxxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "b634d2fb563fb2c7053f3d2831074bac9a06bb53843394bcc4f900ed5fb655e7")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_search_api_mtrl_alpha.png", "e844c8feb07a35c300868ee6bdf72e112523a421933494586bf96015f2a4e4b8")
|
||||
put("res/drawable-hdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "39aa138b792f6c1e1df99e0c1b748406b50aa6da3a864345f11bdedb3c0710a7")
|
||||
put("res/drawable-xxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "74a6c82d92bd4a42ba5a2a8f819c2dfee5fecfdf29a8539b920f0a239f27418f")
|
||||
put("res/drawable-xxxhdpi-v4/abc_btn_check_to_on_mtrl_000.png", "e88c61d8f5bdbc2b21024a8f2fd69aae679d3ea4f77b4d6e66dc95a792ec6365")
|
||||
put("res/layout-v21/abc_screen_toolbar.xml", "69892161f0b97e9ba9c06b00b91c0c725fd9289caac51ebdafee7f2455d72197")
|
||||
put("res/drawable-hdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "9aca579e09ed6d8a1eb275b59248755f8a117c05b96178232d31d66ea327971c")
|
||||
put("res/drawable-land-xhdpi-v4/screen.png", "0399d3e0e58cef3d3c47c892e9f1ffcfc43b96e937c67a891a42bdbdd7f9a23d")
|
||||
put("res/mipmap-ldpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734")
|
||||
put("res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "5080b07eb486ba05ede737b567eb2bb89d7b79a3edb5c2ca8141c6eba012b75d")
|
||||
put("res/drawable-mdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "2be74c8cb7b5f25da58130799a22ca9435de80153a33a7dbbd3eaf9c8a96cb1d")
|
||||
put("res/drawable-ldrtl-xxxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "160f6327240b59ec3534eefe0d4ea6dbd585e01d1357d870e910c5594016dc96")
|
||||
put("res/drawable-hdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "c7a19902a30c635c4708adc2e0c2911fbdb30fa8baf8878e96f44a8c9c012f65")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "1eb6da19924fabb0116fca45176fe1f4061708f5451e9844de332cb1e413e114")
|
||||
put("res/drawable/abc_list_selector_holo_light.xml", "a3704ca7cc4c35254cc4719f675a8fa522935bd6f5bba1d0c94f6b3a091ed552")
|
||||
put("res/drawable-xhdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "02ad0ea6ff5a560e947452c7d64f679afa2056b44f86ea3b707a1cb175c2ceb0")
|
||||
put("res/drawable-mdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "dc02a2cc31bace5e8d4930f24af0e6f3de60ea3d991772c107a45f2af56bf957")
|
||||
put("res/drawable-mdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "18c6c39be0e5d67eed7006dd262f3e850f1d83aca242d836a208fb1f1e3638eb")
|
||||
put("res/drawable-ldrtl-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "641ad19d3467a8e5c4d9714eb7581608de2843f19e9a902a4e9223be0661fb87")
|
||||
put("res/drawable-hdpi-v4/abc_popup_background_mtrl_mult.9.png", "ad4d6c2d1a343d36781e16e81479c4a4cf75829d1ba2dce23443ed22f909540c")
|
||||
put("res/drawable-hdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "97caeaf819a086da7ff269296eda8b3899ccda2859820553772c2a9ed92b0c1d")
|
||||
put("res/drawable-xhdpi-v4/abc_list_focused_holo.9.png", "671d27e5a4b42111174b888d57118e05075a556d703bd54f9335202d2a416791")
|
||||
put("res/drawable-ldrtl-mdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "fe3e9afafacc6d85d8c92bdb5032d84652432d759570909847fa690b67efc137")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "a3c71fec76e45cf16b5b6e508646217539fe6467c5e9fc27f9458b15c6df45fe")
|
||||
put("res/drawable-hdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "239d602c9c65f156fe024613207955ac8fbdd395ae6ec3efa51be98e3a4473f4")
|
||||
put("res/drawable-hdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "2ab0d18aa581997b2d36ac191151273995b212d3ac2e378ee2497e8aee81bfa6")
|
||||
put("res/drawable-xxhdpi-v4/abc_switch_track_mtrl_alpha.9.png", "4cdcafce259e3beb0cdcfccd969f6b18fdab421870934715cb4e0272230a36c8")
|
||||
put("res/drawable-hdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "b769fc3384d88cbdcfdcfa316bc372a52b3e3976b87590bb2820e11edb9a00d9")
|
||||
put("res/drawable-mdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "9f68c3256c42aae292504cb7ede8935e1f9c82bdb31eb0d2a55ca0c62ab810b6")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "c3402146e7228cb12772aad91fd071bcab4d668387a5da9b9a47bba8a5bfc7f6")
|
||||
put("res/drawable-v21/abc_action_bar_item_background_material.xml", "8edbac0403d1abd530ee523971573d4c306a7a303f009f0b7f3befebab52e9db")
|
||||
put("res/drawable-hdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "9ab4435bd62894ca412cd86419c477cd66b14a6b660f316c249a88066006d505")
|
||||
put("res/xml/file_paths.xml", "e5a20dcc6765209234b2de6098cb99efa270649319199eb00453d1cecded9b6d")
|
||||
put("res/layout/abc_dialog_title_material.xml", "b3393728c23229c6b0d9600f2240fc8616a925a0159628211f4a744361da9c2d")
|
||||
put("res/layout/notification_template_part_time.xml", "e3fff5c2c216ebb732be9bbf9c214288548da591f9927752010790bce243223d")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_clear_mtrl_alpha.png", "287078c9586e942bd0d2a3d3e82db9d8d83f63bcf0d38e8ea148117418b313b7")
|
||||
put("res/drawable-hdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "bcf9e688a1710c320c24f682ad26d12cd26b4b5e16e3a01a3ee690e486d57a95")
|
||||
put("res/drawable-mdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "387c23e784744e75a3611ce17ed156f1c18852c6b025a94d16b13c0b10ac4410")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_clear_mtrl_alpha.png", "93b3126fb85e3934cdef24c5f695c455688056171d14b462245fdc5febb10694")
|
||||
put("res/drawable/abc_dialog_material_background_dark.xml", "cb9ce61156ab53d6344566fa385d7dfefd75af30c37cbddfbe9446515e5933cb")
|
||||
put("res/drawable-port-xxhdpi-v4/screen.png", "ad1ac21edfdff36e6ca67afe850caa99d13e026c8fb09cf98d935ca493870ba0")
|
||||
put("res/anim/abc_shrink_fade_out_from_bottom.xml", "e1192ab74c1bf01b33726f22ec05ed5329cb20079c5c77e5dad18f9692da96d0")
|
||||
put("res/drawable-hdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "cb93f48592ac219831b184b9e8d667edb48d13baca83b16fffef49dcc67b91f7")
|
||||
put("res/drawable-xxhdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png", "68f02964d1eb0e1095cf76030bdb4ccc4d3bed858dd0819cbb2d6608b3c5e82f")
|
||||
put("res/drawable/abc_dialog_material_background_light.xml", "ec2c6cd2099d681aa8423c6e6bcbb0a0b5c44fcdc56ac8b63cd724b287735003")
|
||||
put("res/layout-v17/abc_alert_dialog_button_bar_material.xml", "ad4bedea15c039291e4f5668b3114ca8723ddb2da042568831b37815e6100c60")
|
||||
put("res/drawable-hdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "774a7c9a0175ac0d1e3e87f296e62b6b1597aceea8486d94e4cebce3875b003e")
|
||||
put("res/layout/select_dialog_multichoice_material.xml", "137466266c666166c43bc5b02fbbe986c6bfd7ada19b0fd6ab30b4b01050f5a6")
|
||||
put("res/drawable-mdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "00e20ad3da7eedc2389f24c48bab875075c6dcfc902c054cd0e89f9bee02f92a")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "2cf5ecdf6620a0e3a9f9ce3ce787e25cc05fdbe9c1d3c51dac2fe6f496074cab")
|
||||
put("res/drawable-xxxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "ebcb76558e45a7f4c6692aa617173d40c41030034646510f0c76760e9b991d77")
|
||||
put("res/drawable-xhdpi-v4/abc_popup_background_mtrl_mult.9.png", "fd1d8ff15f443fed99f64bb2de4c8e59148e7f45f0272c9388a624b3fdb5bf47")
|
||||
put("res/drawable-xxhdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "450bf8ea02097bc27ac7e21fa40fdc4e4ad521faf2e6bd1765711cd2c02c59b5")
|
||||
put("res/layout/abc_action_mode_bar.xml", "26e44e7cff3de1aa7d927acff7aeb0a4cddb0f872a277710944c9a85c984269e")
|
||||
put("res/anim/abc_popup_enter.xml", "414f580b4dc1f808c834f1d90dd2c7db9b7660e6465ec5420b70e55405fb9c9f")
|
||||
put("res/drawable-hdpi-v4/abc_list_focused_holo.9.png", "a2f1a37af8e699320a2fadbe54eeaa6112d5a3522d57d682edf04fe97be3b47e")
|
||||
put("res/layout/notification_media_action.xml", "3d27932335f2e4d9ee922909174332f6b0b40277705dd3f5df32deb5dcddd31b")
|
||||
put("res/drawable-land-hdpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770")
|
||||
put("res/drawable-xhdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png", "4aa8bc81c7e08307255e73c16afedd2c97fd37a65dbdf2fdbbf9f9e37aff3758")
|
||||
put("res/drawable-xhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "afedf965914f9e0bb79f57f9083c4f44972f2e8c69f2645a9e12dfe648f22db5")
|
||||
put("res/drawable-mdpi-v4/ic_action_remove.png", "fd5b864e446c631b5a41f5d40ff3e522c3073f8d651eb7fa1eb60b80cb42c85d")
|
||||
put("res/drawable-xxhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "4bb4e2ec69ded3e748a26522b90a3d1318795687787374a05dc711f2925b4464")
|
||||
put("res/drawable-xxhdpi-v4/abc_list_pressed_holo_light.9.png", "f0bc6f14305322192eb32eddcd3649025c712f8eec3cfcb5b298d7f4708073f5")
|
||||
put("res/drawable-port-hdpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770")
|
||||
put("res/drawable/abc_item_background_holo_dark.xml", "6cc5a10d3387923b2ca66b5daa16b8524201f21ffac8236036b59c76a3e6ef44")
|
||||
put("res/drawable-xxhdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "f4812c6538630315d02cc3a182ba3fcfb85868a2aa98318ec5dd63677351f872")
|
||||
put("res/drawable-mdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "66055f4b925daef0e5640a8e3075cd7b176a34da388085d52857d5c6f38ba632")
|
||||
put("res/layout-v17/abc_alert_dialog_material.xml", "a198b24b9135e8a03d27d288ef4a9cf012b9a63c21f7ccf4da61de80dad7d62a")
|
||||
put("res/drawable-ldrtl-xxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "ee0e3c601dbf603d7001b7ef4cbdb237a959734c9649dd211b0bf615b6ff4de1")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "8c6e2a3c33890d72be345310e6e531778c25b9c9b143762c6c2bdee88f5fb0d0")
|
||||
put("res/mipmap-mdpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734")
|
||||
put("res/layout/abc_list_menu_item_checkbox.xml", "6b742ec907cd9ce87bbadfdc83b34e570a6ae1ec760364ddd17c3365e89066e8")
|
||||
put("res/color/abc_secondary_text_material_dark.xml", "4e82e40e26ae5947329110f135b30ee5883d633689f3ab3f20ea0f68f1737efc")
|
||||
put("res/layout-v17/abc_dialog_title_material.xml", "78fa8892acc354daa4b030b0d49767262f27b659960b81f92c1a3a129d19fa1a")
|
||||
put("res/drawable-xxxhdpi-v4/abc_btn_check_to_on_mtrl_015.png", "52a0358e54439373ca39f99cd83cf3a7be73c257f07fae83edd83c9b4891895d")
|
||||
put("res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "7442cda653acbacdf9aef256aa191e5ebb6071a1747312aec45e1db8252a3cfb")
|
||||
put("res/drawable-port-xhdpi-v4/screen.png", "0399d3e0e58cef3d3c47c892e9f1ffcfc43b96e937c67a891a42bdbdd7f9a23d")
|
||||
put("res/drawable-xxhdpi-v4/abc_btn_check_to_on_mtrl_000.png", "f5ebab3a4b8712f651cd7e42a17a4d0577c3e3deb89e43123d6180c7f6d5137b")
|
||||
put("res/layout/abc_search_view.xml", "d86ff6a808854f2071c1c9eba22b1d6a8b984319acf108070c3cec77d47f82e3")
|
||||
put("res/drawable-mdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "1db8f8301b62f43b1f05faf082944e8be93eaa9b54669a406321e1b7ae9b05e8")
|
||||
put("res/drawable-xxhdpi-v4/abc_popup_background_mtrl_mult.9.png", "4268b15d37eb27ec7ac1bb221a57000cede389fd34beaf75e268afb380901152")
|
||||
put("res/drawable-mdpi-v4/abc_list_divider_mtrl_alpha.9.png", "0ef7b3d9f9d890a48e4fccc0db07315bd6fcca7049452e20a27a69bd22080415")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "9b6ad33830a9fa48796632fffd0a5386cce8ead83fa7265fb1a96bd44d479306")
|
||||
put("res/layout/notification_template_media.xml", "208f7b22a6553723c1c60d971fd5fed0400ea1a180f265353a33d2a9bd5436ff")
|
||||
put("res/drawable-mdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "5f997c46e51b3726b57a582f13663aedfeed55e4e61fcfe6dcf9999ead38f1d4")
|
||||
put("res/drawable-hdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "44b4d2e07f32af386071fac95b80581e3c955b5e2281749d42774de9dfc87535")
|
||||
put("res/drawable/abc_ratingbar_full_material.xml", "c323ab086275b807a25d041108bf3b1e73b1291bd276f3fbfbfe0f298766974e")
|
||||
put("res/drawable-hdpi-v4/abc_ic_clear_mtrl_alpha.png", "e394bbb629366e5727227fdee2d3e3ad9bc5759950a8b358bbf5a4777ffe968d")
|
||||
put("res/layout/notification_template_lines.xml", "0f6ca4f3efcbfd75584343039fe374222e89162ae1895413b76a930f4554d0df")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "94b80df3ae6d8fbbb43ca98f062c7083ffbdf604715e1610a9a622f927a6fcdb")
|
||||
put("res/layout/support_simple_spinner_dropdown_item.xml", "596b32eee9c6dc78af72f86bd82015d755778399ab4de6e9520706dd19bee4ef")
|
||||
put("res/drawable-xhdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "052762422b60e16802dd4dcdbc998f9c2ef911049e086592650faf895ec2bcd4")
|
||||
put("res/drawable-ldrtl-xxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "ebd1e1432b6a54cc6161da7f69837e8fa45ed95eb196b421d693fffb040be7be")
|
||||
put("res/drawable-xxxhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "079843966864bf4099457cb72f1febe7c57bb5a70bf0f544a9169a078b7a5b01")
|
||||
put("res/drawable-xxxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "4b787425a2b1eabb721cd99bd23ef235db80f41900a66028c2872fa068011078")
|
||||
put("res/drawable-ldrtl-mdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "0f079710ca543c4c7042d0a463764c07ba6ba4bda26ec9b625c26485c98fc569")
|
||||
put("res/drawable-xxhdpi-v4/abc_list_focused_holo.9.png", "8c6846f4105ef1c02107e842142e9ce60b08819296e9510b2d0c12cf4560c5e1")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "d523fe7dc93908f07a61569bfc52f4be6e9c6033775d74394d18566832ccc408")
|
||||
put("res/drawable-xxhdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "0eedc51deea0c0f3f675a400d667eb476553b92cbd12683b6154e36306031c19")
|
||||
put("res/layout/abc_select_dialog_material.xml", "15a1a201de1c2f663436eecee51b80545aa6f5f93811a45b324165a5e8716435")
|
||||
put("res/anim/abc_slide_in_top.xml", "05cc140039277e0a2299e4d1650ee39f7d12ff5e09cecf84067c9f2f340ecd06")
|
||||
put("res/drawable-hdpi-v4/abc_list_divider_mtrl_alpha.9.png", "0ef7b3d9f9d890a48e4fccc0db07315bd6fcca7049452e20a27a69bd22080415")
|
||||
put("res/drawable-hdpi-v4/abc_list_pressed_holo_dark.9.png", "24e31720f77bc442bfd450d80747a99db2df3675a53447a291622006df52815a")
|
||||
put("res/drawable-mdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "cacfc7acb35eb382effa1b8ee9881e1be435988bc98db9bf08199c3b6f8bca82")
|
||||
put("res/layout-v17/notification_template_part_time.xml", "ee3fefac46bdfe73e01c2775944cd3cc2acab99d7c657efe20a3cc56a08a4805")
|
||||
put("res/drawable-xxhdpi-v4/abc_btn_check_to_on_mtrl_015.png", "71a2ba63a5d4e91f7c14324fa1c3eb9290b73cb7b3c7828018d05adb1cfaab45")
|
||||
put("res/drawable-xhdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png", "e00c96c08036d89fc1fbe27df31257ce08ecaf2e06806786f3751ba7c970ff91")
|
||||
put("res/color/abc_primary_text_material_dark.xml", "116a77ff0f38762bc4c47fcd4e4157490dc970625415ddebc2dfeb9aa17970f5")
|
||||
put("res/drawable-xhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "2ae7387b4db93f8ecbc01d288b752d41373ce7b7d808969fca1e3b2e9d049219")
|
||||
put("res/drawable-mdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "c618fafa6107721f6b07922ab06ed1922074412c7963dfcc9e34e1db93daacd5")
|
||||
put("res/layout-v17/notification_template_part_chronometer.xml", "5e800e8032e365bd2f4f2964bbd3f2ec2ea443201ca06c47590bb346e72929cd")
|
||||
put("res/drawable-xxxhdpi-v4/abc_switch_track_mtrl_alpha.9.png", "add9abce47185fdac6290fa13cc9cbf46cf4148c7dbf4f3147f0e7b821ca223f")
|
||||
put("res/drawable-ldrtl-hdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "8945707810ae4c8b568981bdcb74fcc92998ea09045f18649d587ea75ed8b985")
|
||||
put("res/color-v11/abc_background_cache_hint_selector_material_light.xml", "ebf2196624ef10188cd1a0935b5426b2fdf8232a362277a57d09c4a1f2c507da")
|
||||
put("res/drawable-mdpi-v4/abc_popup_background_mtrl_mult.9.png", "9e96a642ab9139cca3fb822ce199c711443087b5a7463e0c7fed012d9a773285")
|
||||
put("res/layout/abc_popup_menu_item_layout.xml", "88986f7cc025876a7093ff01e98f0df8a8f452a43bc1b6438b089d4d2036cea6")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_search_api_mtrl_alpha.png", "269f388a6a672af16e49203bce389020f51da34b43e7117e954c3cc0ab96c840")
|
||||
put("res/drawable-xxhdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "523733ec9c1ed308943cfb03770d64909c02ea6e866d535d389f9ae663a4c78f")
|
||||
put("res/layout/abc_expanded_menu_layout.xml", "9d026182191a5c46211ba40fd2abf85483bfec033e28d21895195aa27b5aea0c")
|
||||
put("res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "8dd453207afc153e8f1712fb630568e05cf2697fa22ca1789e53c247de2cb960")
|
||||
put("res/drawable-xhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "3c93dc6550e031dec53f2c1646b46fe52e6c574a7b5eddd33d029100363df916")
|
||||
put("res/xml/provider_paths.xml", "2e4306b5c4d5603f5db9c29b176c253e9420b46be253daac581c4c98f1aa108b")
|
||||
put("res/drawable/abc_textfield_search_material.xml", "820b5412de01a1488720d18aacbeceadd07a0288a2d13dd3af3d977590a6ff9d")
|
||||
put("res/drawable-xhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "c6a403e2ee84f5ba230fc1acaa5c6b87b9defab26f7b679696ac885b7dfe4a21")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "bb54f3ff22d6e6bcaaaeef3e9b0765137485f924a8b7888e7dc2b9b826832913")
|
||||
put("res/mipmap-xxhdpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734")
|
||||
put("res/layout/abc_screen_toolbar.xml", "f1670f2cd6c6dcb86f3c50b0e840a0195322cdbcd460e46828806ca1cd918ba0")
|
||||
put("res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "9dc401d6dcb691b4904d007d4c57be4af0b38a2b103fafca5812ac02b6f0e0cd")
|
||||
put("res/drawable/abc_btn_borderless_material.xml", "0822257cdc5124d811dfb13ae57f242e4ae550abce462250231c0befa29e082a")
|
||||
put("res/layout/abc_alert_dialog_material.xml", "3825181f8cffc231f4fa7fcfc3078eae492f19d39c202de2bcd21c537a59d7ac")
|
||||
put("res/drawable-v23/abc_control_background_material.xml", "3ce4a969a079c570bb634572d111be190443a90f0fa754e0f99e645dc6c25a82")
|
||||
put("res/color-v23/abc_color_highlight_material.xml", "b476ac65ddf4fcc665d5d9225365a6bcb613b186649a6490232aa4c458fa7c55")
|
||||
put("res/drawable-xxhdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "2310172cf1c75ca08b63baa38d29b6a145da35e9bd23ac0b78d2571f00d9d88a")
|
||||
put("res/drawable-v21/abc_btn_colored_material.xml", "ede942fcb0f3325b45ed32e0c76ab0c760229d2a4201455c245e04fd8d6ff01a")
|
||||
put("res/drawable-xxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "20f88839f937925401106c71e57f7e4a24636dbb5ed714ae702bac11c05a0fe7")
|
||||
put("res/drawable-mdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "5c621922ae954f1c7b17a2c81e82caf8e26c3c8f7bb1f494536e12996298befe")
|
||||
put("res/drawable-xxhdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "a8252f52638c80df119db031626198a4a8d4b32ee618e96c3a36b9b946e26894")
|
||||
put("res/drawable/abc_list_selector_holo_dark.xml", "c0a8c5fa064df16c8130e6c1b89c407ca3e6907a77138f9354fd7f279a309cb5")
|
||||
put("res/drawable-mdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png", "91bc27962779745e35aa681dc7a74fd5e829f090e59d0ff9557bab37e2ca2d40")
|
||||
put("res/drawable-mdpi-v4/abc_list_longpressed_holo.9.png", "dbc0af61f440bb3f5382b8ea3fa578659f76c7a6aef620a68afc43a909f2e0e5")
|
||||
put("res/drawable-xxhdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "0eed99d0a5e36a468b557605e6ccea495c029f3569844a45f4b5d8dadd77d4a7")
|
||||
put("res/layout-v17/notification_template_big_media.xml", "1eac34b2099c68d22e45f05253ad6d357dde52b57765cc6bf787fcb12e03265b")
|
||||
put("res/drawable-xxhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "75c6f650f790493b89c50d6a3bc46bcecc677291a1ac1513b067cb4dd73aafa6")
|
||||
put("res/drawable/abc_tab_indicator_material.xml", "8663108d80dc207cc2d9e2e89dfe75ac1b68f0b804546874f8363a7fa797a90f")
|
||||
put("res/drawable-xxxhdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "6002eb56783b956b5f91e6ddffaf36cc867402a01a225e627b4c25c8d21a4e23")
|
||||
put("res/drawable-mdpi-v4/ic_action_next_item.png", "c01422fff1e45b864e7cc9b807a83d80868dac2bd0113e138385076b1a6fbf6d")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "624af94ddb9b168bf22243043429772bede14c779e771dba98c6cc9ba597c4c3")
|
||||
put("res/drawable-xhdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "3d7bbf47c8ac2b75977a579ca1aa2c12be8494f916450e971cf172bbc8a1f311")
|
||||
put("res/drawable-xxhdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png", "7d230ee7f0709fe3016a360538d493f0a3b565337e576dccb1e798985fc473d9")
|
||||
put("res/drawable-xxhdpi-v4/abc_list_selector_disabled_holo_light.9.png", "550e8f5f4a6004c0ff14c681b20ae675c6943e5ee299da36939f790fa8818fe0")
|
||||
put("res/layout-v17/select_dialog_multichoice_material.xml", "b8dd03f6cdaddda24427698c320c9fe7fbdeda4e360a67f901cc99f6b676b0b2")
|
||||
put("res/drawable-xhdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "247152ba9b093d65d697d071cc7e986ac7e64c4ec95cf42c40b6f054ef9bb661")
|
||||
put("res/layout/abc_action_bar_up_container.xml", "0903525f885f6aba13a17daf5be0355c455689dac8839418dc57908c8c7008a4")
|
||||
put("res/drawable/abc_spinner_textfield_background_material.xml", "a875de881d4296c070560ae4208bb00810874ec9252e5569ddad3c50e5e580a7")
|
||||
put("res/drawable-hdpi-v4/abc_ic_go_search_api_mtrl_alpha.png", "8eefaa5bbcce1a47294ada4eca76c92dec81913de9ab2d4cd09c1bf9fa10990f")
|
||||
put("res/drawable-hdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png", "180a880eb2c603e1e1c903a2cd9cdbf8afa6fc2fe0bf6618d2f9b57b637832ac")
|
||||
put("res/drawable-xhdpi-v4/ic_action_previous_item.png", "1dc3b456866e6c9a612ef9eec0b900394bae784c84af4e9dbdadd07d02cd394d")
|
||||
put("res/layout/abc_activity_chooser_view_list_item.xml", "0c45295e533ffa94b98572b6b782872524cc25f3021d43646b62af4e66f3391e")
|
||||
put("res/drawable-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "b948c74fe67d905b365ad3dc96a6ee416c9927dcadf3c555b5b2359540100b18")
|
||||
put("res/drawable-mdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "9c05e26f9e74ce6d54e1e9e3d0fc1d6e7353ce7515e1195843fc627ad734156b")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "7fb735d88839bff8190cdcadd7896326365456c85561d098d1d9ab98bf291838")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "a55f232779d32baeea6c7ed6c84c6e985d3288c6d244e29cc8856d9d0334f32c")
|
||||
put("res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "ffb431a4128e5aac93bf838154e272768a6dd51ab36a61517c95d53700daa0b9")
|
||||
put("res/layout/abc_action_bar_title_item.xml", "9203c3aee1dc30c22c3909a126c087f20507a27e0e27c09d0f86de2303b0a64c")
|
||||
put("res/drawable-port-mdpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770")
|
||||
put("res/drawable/abc_btn_check_material.xml", "12e9ca66114da2c67fc8f8ea38b4f28de73155154c2d15f9e7cc9a0fb4e2a4e6")
|
||||
put("res/layout/notification_media_cancel_action.xml", "109cddab44f02e57fac23430d5ecd17a54fac1f5fad5444887cb2c68be84fb80")
|
||||
put("res/drawable-ldrtl-mdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "3a283a4f4cc377084337680f3d3c693259902fc8793f5395afdfeaa0bf9ca105")
|
||||
put("res/drawable-ldrtl-xhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "3cc16a0cc86ab8c137f9355a5577927a95caba18565b657d815b76461de3962e")
|
||||
put("res/drawable-mdpi-v4/abc_ic_go_search_api_mtrl_alpha.png", "acf23a1bc642062f1f93f94247463c4077cd00d48521c3609268dff657c488ac")
|
||||
put("res/drawable-xxhdpi-v4/ic_action_previous_item.png", "0f18602b3446953de962a1a8d412db3532d2ef180ec0bc8b3d33fd35e8cbb5cc")
|
||||
put("res/raw/zxing_beep.ogg", "9bd6f5856d9162d4a41c016976edc4bfc2dfbbff9d98f05a362a50fa678dd5bc")
|
||||
put("res/drawable-port-ldpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770")
|
||||
put("res/drawable-mdpi-v4/abc_ic_clear_mtrl_alpha.png", "c550947a9eab1396c706ba97a39b3e4ce51c91329a5e597f6d6647bfece68fed")
|
||||
put("res/drawable-xxhdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "05b76fbf46593122597b8b4e2fd2358fa0734a9fbc2d24f5198ee2a72e77a9e9")
|
||||
put("res/drawable-xxhdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "025d8a1155d2ce4769e307a89ff7ec6c175280df90ec51a8c61cea4551992cef")
|
||||
put("res/drawable-xxxhdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "53340b0b0f529b26db6af632a0112708233bf312448c5b89a1357b2cd70b3f42")
|
||||
put("res/anim/abc_slide_out_bottom.xml", "e33c8ff0e038ba561eac7101963096094d58b5f1a748c1067bad5290328148df")
|
||||
put("res/drawable-hdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "e80e3db92ba1b47e32b7279a00778aac3c68150f88a4158941096adea34561bb")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "cac329555c22a8b70581bc59c12c1c3e19c86748dd5a9e2c1251de16ee579170")
|
||||
put("res/drawable-hdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "ea50d85209d909ecd07013b992929444b7f51fd22da25a6be2936631ad1d28c4")
|
||||
put("res/layout-v17/notification_template_lines.xml", "cb5c99d9244a9d994903ce2c3e12c346be80c8661f87845cdd609ed377a13b5c")
|
||||
put("res/drawable-mdpi-v4/abc_switch_track_mtrl_alpha.9.png", "7b73c914c459938dc3257fa1151c736609eb37bb437ee42071b81eb4860bdc04")
|
||||
put("res/drawable/abc_edit_text_material.xml", "3ee45694043973b0dd0f43c29cae923c63f053af6cab0d5b501162153f7f84a5")
|
||||
put("res/drawable-ldrtl-xhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "5d01a80f82b4c5874f20d78183037fb55504c6c3384458ad29b4535bdb951cac")
|
||||
put("res/drawable-mdpi-v4/abc_btn_check_to_on_mtrl_015.png", "d33c0277bfd52f3b32daac3c30a420d7fc0a7214addb30dfe3b44dfead940b11")
|
||||
put("res/drawable/abc_btn_radio_material.xml", "206c4cd4d38f53ee940c2ed149cdb5d7483dda1d89bcdb7d2b68adb5d6392846")
|
||||
put("res/drawable-xhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "915cfe2e2a1f9f2ad498669b0b1c6ec4a26735b7e690c4fd6b8339aa1747e5d7")
|
||||
put("res/drawable-hdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "c0e4c8fdbf4fef50f8a7033e529c3ef8da2c994b3567d4e9d89cfa099e7ac15b")
|
||||
put("res/drawable-xhdpi-v4/abc_btn_check_to_on_mtrl_015.png", "2fa524cda78d7ebd66164a896a042277b7a56b8658fb318fc700a114cb04f7b2")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_search_api_mtrl_alpha.png", "0bdd69dcc421f49c9feb0baa908694baa968464dce0f09b55a8a8add3724e996")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "1c515bd153b3f9d1eb1bb53c2dcb0c168ed3cf0763986ef1652cd39aa3a0bbc2")
|
||||
put("res/layout/notification_template_big_media.xml", "5195d15e19b8bab1fb9fc2973708cb3968f25f066758b854801b3ff7674b32bf")
|
||||
put("res/drawable-xxhdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "bfa384ba37a3e1024b738e0a82a7890f012f1a3291307135f7c819d0c9c3d425")
|
||||
put("res/drawable-ldrtl-xhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "e89ed0715d5e67ab55ddcb3f2a56b41ac50d2e6a3b9216fea4d8ee23693c8f64")
|
||||
put("res/drawable-ldrtl-xxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "173404ec34c97c2142f4955ac0950606649b195fab48df5e8f33560cbd9c5654")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_go_search_api_mtrl_alpha.png", "9b699740ec1aa941bf4af584ae2fc2a3a37f3036769736b8885e1a682330c248")
|
||||
put("res/drawable-xxhdpi-v4/abc_list_longpressed_holo.9.png", "44c29766b2d88b78a71393b3e3a771ca7eef5f552630c6074f33d6291241d0b9")
|
||||
put("res/drawable-mdpi-v4/abc_list_selector_disabled_holo_light.9.png", "ae964252406ac9465a442ad66c9fe077e8d16391de4ece7ead6128b97f954362")
|
||||
put("res/drawable-hdpi-v4/abc_list_selector_disabled_holo_light.9.png", "e71502ac4d35951d0e063ef8b76885e27846a2dc0cdc616959d2a513f4852a2d")
|
||||
put("res/anim/abc_grow_fade_in_from_bottom.xml", "a5fbceaa55ddc0aaed2077543b1f32ab7dd4b1de32a8775cac4e65ba09ed04a9")
|
||||
put("res/drawable-land-xxhdpi-v4/screen.png", "ad1ac21edfdff36e6ca67afe850caa99d13e026c8fb09cf98d935ca493870ba0")
|
||||
put("res/drawable-mdpi-v4/abc_ic_search_api_mtrl_alpha.png", "7ad683f3e730c98b12ae85edc0f9b2a61476f1dcf3df411721924d919ae98f5b")
|
||||
put("res/drawable/abc_cab_background_internal_bg.xml", "ec7dc93f92559eb10d1d91fa944e746e95c3c7f03bbffab87ff22c1b3b002f49")
|
||||
put("res/drawable-mdpi-v4/abc_list_pressed_holo_light.9.png", "35c56471c6414f33645a376c0d1a8a126f4cdc728f2344d08bccd3828e810e2e")
|
||||
put("res/drawable/abc_item_background_holo_light.xml", "daf638c66b3fe5f24b5c7038193ca6e0a462663eeec720462d61cb4c61fbbf2c")
|
||||
put("res/color/switch_thumb_material_dark.xml", "2c505932939633d0a61684d98161bde0a05b8c32e967114031335ee96387a76d")
|
||||
put("res/drawable-ldrtl-xhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "fe21f096ab18f8947a27624ae952e86be4ff8d645ae714b653d398f64f8e39ee")
|
||||
put("res/layout/abc_screen_content_include.xml", "bfdb030a0c978bb66333ac0dc6c4748caf3d856835f7370aaa8e06ec3269534c")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "9e81ace23f53713fff9f07794897def554cf72df8bcc0e78e3ca2e1184e75e5b")
|
||||
put("res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_000.png", "5eca0280bd38a6887b6e41765cf5da573133874625001eb90156eaeb35fbe088")
|
||||
put("res/drawable-mdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "16a04f086d374b00ba759124c4a372de58cb1b4eb070db121ca747073fef4745")
|
||||
put("res/drawable/abc_seekbar_track_material.xml", "a209cb89db7acaf32a22ad77209ceccdd2a1c8a401c9d5e0df99961468338f50")
|
||||
put("res/mipmap-hdpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734")
|
||||
put("res/drawable-xxhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "40ae459db3548ac42fb949d69ecd5d3c452866db0fcf4d1b1081dd676a9431ec")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "2c3122e4c041da2a88b5e513d2891edd20ff7dc1216708e2d3b7b14678d08232")
|
||||
put("res/drawable-hdpi-v4/abc_switch_track_mtrl_alpha.9.png", "2c770f581a2ca530edeb27812f9153ca16a7b79f5245e02d6911b534a4c80c5e")
|
||||
put("res/drawable-mdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "4a494c652ef9019cc723afcb39de29a523e72e7b648739900fd786161cd47bf8")
|
||||
put("res/drawable-mdpi-v4/abc_btn_check_to_on_mtrl_000.png", "055d5f87b1bbb6677096bc7eb7eace57e40216cb97f2b2b926d26c1263f9abce")
|
||||
put("res/layout/select_dialog_singlechoice_material.xml", "7f7d5e80a17d35312f1ab8e84da3c0d1a6a1213dbc71ecdbb1e1507f00c2ad07")
|
||||
put("res/drawable-hdpi-v4/ic_action_previous_item.png", "d326359f839c3c87dc22c9c133fcf8a6789eec76f6276300d0e045a5354feb36")
|
||||
put("res/layout/select_dialog_item_material.xml", "16482ac145fdbda41d7eb9fba7020475dd6c96b2e11d70d2bab7c0a8bef0a286")
|
||||
put("res/drawable-xhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "236db6dffdfccab00c623ed956723f19660cacbc9d9e2daa0ecd91e4afc40d2b")
|
||||
put("res/drawable-xxhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "cf8755669eda61de90624c9e21cbce526ca313ca206d7563f5a6066b1876abce")
|
||||
put("res/drawable-ldrtl-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "de207ef50c941d5a22eb48586346228ff5b98c6f0540d6731af06658aeb8ecb7")
|
||||
put("res/drawable-mdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "b1e9c8ff1b5662b3a5c7bab6b223ff14b68fe14e2d097ffc3ccdc3d5859eac88")
|
||||
put("res/drawable-xhdpi-v4/abc_list_longpressed_holo.9.png", "727d78fec3f903a271c0476a50e8c064d28b32f7829b016ae0a07da53b83b281")
|
||||
put("res/layout-v17/notification_template_media.xml", "8bb61dab2c80af12d33d99e23df5c9b604d76294ff0ddb4ffa3c9d880ad2bd35")
|
||||
put("res/drawable-xxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "54b757ced1278d78846c070ae2802ca65f88c17657236e4fd72e46e23a2c9a22")
|
||||
put("res/layout/abc_action_mode_close_item_material.xml", "0b5cff6338e6ce6fd63b8f24846558e269ac39f9e1f70cf6b8e45c1ec67cc260")
|
||||
put("res/drawable-mdpi-v4/abc_list_focused_holo.9.png", "8436624220fba246ae816a3aab379dfd7ff2a484284ab24c3af4989778705bb3")
|
||||
put("res/drawable-xhdpi-v4/ic_action_next_item.png", "9c57cd4759313169898d4f4b69a96bd963c3b2257532d5ed43275b502ca98e64")
|
||||
put("res/drawable/abc_text_cursor_material.xml", "14e09849019f466a6780ffb38bfffdee36c21519c7627f1fdb17224b60e09f6b")
|
||||
put("res/drawable-mdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "1df76313283b0046c002e9792e35b1f4087ef98e79bb757aededc9d27bd54897")
|
||||
put("res/drawable-mdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "4ad77975a92c859163b4f39ba17d01e3bde2d03f1fc2177d113401fa84a67c73")
|
||||
put("res/drawable-xhdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "395c5e56806478d86eb4fae5570163962bc6845971dad92ea29356690511ec56")
|
||||
put("res/layout/abc_list_menu_item_layout.xml", "af7dd160067d2397d998be2399dac8f8fe69b9890825896ffbbd447dc44caba0")
|
||||
put("res/color/abc_secondary_text_material_light.xml", "47407173ce8bc17e8d19577230c43f62e9d5f38cae3d23ebaa92f785e0c4932b")
|
||||
put("res/color/switch_thumb_material_light.xml", "45177bbc0542c30e514408dd4b23c83a17a84a5e594989c0e094c159a39cb721")
|
||||
put("res/drawable-hdpi-v4/abc_list_pressed_holo_light.9.png", "7e12fcbd0c61e037cd1cafe510c307b4074d156adac0022d91f3dcdcae692818")
|
||||
put("res/drawable-xxxhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "307cc34bbb3a22f5ad82decd369e14404d467655097cbbce7b03f6aac0f82264")
|
||||
put("res/drawable-mdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png", "da7b78e6af0d3a21b965cec54ce80c54271a0206d1da3787673f9cf504dbe3a4")
|
||||
put("res/drawable-mdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "aa44c927eb4e9a886438f3b6083c948d8cf67c52b3e5fe034b9720648f5fa78e")
|
||||
put("res/drawable/abc_btn_colored_material.xml", "363cf11a26c4c89772b73bf267da6a7b3229e0831b8eff5998076b12011f5603")
|
||||
put("res/anim/abc_slide_out_top.xml", "2f5ba7fce4651ca8fcf598e8bb6df46b4d32912606c5d20a39de9b0554971105")
|
||||
put("res/layout/abc_list_menu_item_radio.xml", "af7c4fa541f9e90b6ec8d6f8971c1d404cfabb86610bd9054de2b2dd211b33e6")
|
||||
put("res/drawable-xhdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "b15c567058a56d15661e044bb4f8408512d87a79d11779ec8d7189dbb30eaefb")
|
||||
put("res/layout/abc_activity_chooser_view.xml", "dd991ccd05f4240d13c6ad9b685debe84f6801d454b5edd0689a4cc9f1fb27e2")
|
||||
put("res/layout/abc_screen_simple.xml", "b444aa5a73a22ebaa8745ac91729180ef54ce675138dd8bf3f75a649b0fb140d")
|
||||
put("res/layout/zxing_barcode_scanner.xml", "b52f5db8970b2d92e3e5c352741dbb4385df7f0d7270db648842e05950b8e92f")
|
||||
put("res/drawable-mdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "cf2d1096edd99c09a185d330a8a202a35b0d21ec1305df4ae7d359bcf0179b4d")
|
||||
put("res/drawable-mdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "36e3737c04ce9ff9322ea71c2908a36ae532437d0b899f70e4ff33f20f810ba8")
|
||||
put("res/drawable/abc_btn_default_mtrl_shape.xml", "f0f21a53cce548fa7d611f1cded76fbc15b100bee7857ef1e6b073138057dd82")
|
||||
put("res/drawable-mdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "27c227cad79fa831621558b8ad808e28c939fb6dbdfa040ae57136b4b50f19d4")
|
||||
put("res/mipmap-xhdpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734")
|
||||
put("res/drawable-hdpi-v4/abc_ic_search_api_mtrl_alpha.png", "4bf6cadfd829c96627a3cc6bdb894ee48edf7567b341aee4a4c38917e65f7fcd")
|
||||
put("res/drawable/abc_switch_thumb_material.xml", "652d8486916207186bc3bf5155d0d9b437f473c5c58ed793932098bd6f22a207")
|
||||
put("res/drawable-xhdpi-v4/abc_btn_check_to_on_mtrl_000.png", "43ccf1f0a5e9e9ed9a6df33e1b3d378d49dbc8dc60d83b1cdb985e5e48f52497")
|
||||
put("res/drawable-hdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "076be4630bb4f4ba10579cba1b3900e3f3f2286cafcbc15892d97fa4b45a27ce")
|
||||
put("res/layout/abc_action_menu_layout.xml", "78e8b182c9982b18cc3bf705e95511e4ef4554b1f15c91d4c443b62587547f97")
|
||||
put("res/layout/notification_template_big_media_narrow.xml", "edc0530f0084410597bedd5623e0cf8e0f1d0f2180ee3377e85ebc3ce7966968")
|
||||
put("res/drawable-hdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png", "b6ed0b4f4e4f64989a78969a49ddf9eb7c2f56a9473691c080d8fc10154f8d73")
|
||||
put("res/drawable-hdpi-v4/ic_action_remove.png", "8ed4fff301384afff335f560209a5216bcf90946f26421eca413aadb07a01d6c")
|
||||
put("res/layout/abc_list_menu_item_icon.xml", "69c2869ea56579b9d02e761d4cf817870b080e9229a32dd4f30ab59255e2fc95")
|
||||
put("res/drawable-mdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "7d15a2bdf032c5ae801f762f92104a30d247a83529ac91c2a5900a7b17307f8b")
|
||||
put("res/drawable-xhdpi-v4/abc_list_divider_mtrl_alpha.9.png", "0ef7b3d9f9d890a48e4fccc0db07315bd6fcca7049452e20a27a69bd22080415")
|
||||
`
|
||||
let arr = str.split(/\n/ig).filter(v => v.trim())
|
||||
let arr2 = arr.map(v => {
|
||||
// console.log(v)
|
||||
v = v.trim().replace(/\s*put\(/, '').replace(/\)$/, '')
|
||||
vArr = v.split(', ')
|
||||
return { file: vArr[0].replace(/\"/ig, ''), hash: vArr[1].replace(/\"/ig, '') }
|
||||
})
|
||||
return arr2
|
||||
}
|
||||
|
||||
helpers.getPlatformsList(context).forEach(function (platform) {
|
||||
var platformPath = path.join(projectRoot, 'platforms', platform);
|
||||
var platformApi = platforms.getPlatformApi(platform, platformPath);
|
||||
var platformInfo = platformApi.getPlatformInfo();
|
||||
var platformRes = platformInfo.locations.res;
|
||||
var source = helpers.getFileMapContent(context, platform, 'ResIntegrity');
|
||||
var content = source.content;
|
||||
|
||||
hashes = getHashList()
|
||||
|
||||
if (platform === 'android') {
|
||||
content = content.replace(/\s*put\("[^"]+",\s"[^"]{64}"\);/g, '')
|
||||
.replace(/hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/, function (match, group) {
|
||||
return match.replace(group, '()\n' + tab());
|
||||
})
|
||||
.replace(/hashList\s*=.+\s*new.*(\(.*\))/, function (match, group) {
|
||||
var replace = match.replace(group, '(' + (hashes.length || '') + ')');
|
||||
if (hashes.length) {
|
||||
replace += ' {{\n' + tab();
|
||||
hashes.forEach(function (h) {
|
||||
replace += tab(2) + 'put("' + h.file + '", "' + h.hash + '");\n' + tab();
|
||||
});
|
||||
replace += tab() + '}}';
|
||||
}
|
||||
return replace;
|
||||
});
|
||||
// console.log(content)
|
||||
try {
|
||||
fs.writeFileSync(source.path, content, 'utf-8');
|
||||
} catch (e) {
|
||||
helpers.exit('Unable to write java class source at path ' + source.path, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function tab(size) {
|
||||
var str = '';
|
||||
for (var i = 0; i < (size || 1); i++) {
|
||||
str += ' ';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
};
|
92
src/android/com/cescit/integrity/ApkIntegrity.java
Normal file
92
src/android/com/cescit/integrity/ApkIntegrity.java
Normal file
@ -0,0 +1,92 @@
|
||||
package com.cescit.integrity;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import android.content.Context;
|
||||
import java.io.File;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
// apk完整性检验
|
||||
class ApkIntegrity {
|
||||
|
||||
private static final String MESSAGE_DIGEST_ALGORITHM = "SHA-256";
|
||||
private static final String ASSETS_BASE_PATH = "";
|
||||
|
||||
// Collections.unmodifiableMap 使得返回的内容只能只读访问
|
||||
private static final Map<String, String> hashList = Collections.unmodifiableMap(
|
||||
new HashMap<String, String>()
|
||||
);
|
||||
|
||||
public static JSONObject check(Context context) throws Exception {
|
||||
JSONObject result = new JSONObject();
|
||||
Map<String, String> nowHashList = getHashMap(context);
|
||||
String ret = HttpUtil.getHttpRequestData((String) Config.getConfig("APK_HASH_URL"));
|
||||
JSONObject obj = new JSONObject(ret);
|
||||
String upHash = obj.getString("apk");
|
||||
String nowHash = nowHashList.get("apk");
|
||||
if (!upHash.equals(nowHash)) {
|
||||
throw new Exception("Content of APK has been tampered");
|
||||
}
|
||||
result.put("res conunt", hashList.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
// 获取文件对应的hash值
|
||||
public static Map getHashMap(Context context) throws Exception{
|
||||
File file = new File(context.getPackageCodePath());
|
||||
InputStream fis = new FileInputStream(file);
|
||||
String fileHash = getFileHash(fis);
|
||||
// res资源(路径:文件hash)键值对
|
||||
Map<String, String> nowHashList = new HashMap<String,String>();
|
||||
nowHashList.put("apk", fileHash);
|
||||
return nowHashList;
|
||||
}
|
||||
|
||||
// 获取Res文件对应的hash值构造的String
|
||||
public static String getHashString(Context context) throws Exception{
|
||||
Map<String, String> nowHashList = getHashMap(context);
|
||||
String str = "";
|
||||
// 遍历对比文件hash
|
||||
for (Map.Entry<String, String> entry : nowHashList.entrySet()) {
|
||||
String fileName = entry.getKey();
|
||||
String presetHash = entry.getValue();
|
||||
if (!presetHash.equals("")) {
|
||||
str += "put(\"" + fileName + "\", \"" + presetHash + "\");";
|
||||
}
|
||||
}
|
||||
// 用默认字符编码解码字符串。
|
||||
byte[] bs = str.getBytes();
|
||||
str = new String(bs, StandardCharsets.UTF_8);
|
||||
return str;
|
||||
}
|
||||
|
||||
private static String getFileHash(InputStream file) throws IOException, NoSuchAlgorithmException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int nRead;
|
||||
byte[] data = new byte[16384];
|
||||
while ((nRead = file.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
buffer.flush();
|
||||
MessageDigest digest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM);
|
||||
byte[] hashBytes = digest.digest(buffer.toByteArray());
|
||||
StringBuffer hexString = new StringBuffer();
|
||||
for (int i = 0; i < hashBytes.length; i++) {
|
||||
if ((0xff & hashBytes[i]) < 0x10) {
|
||||
hexString.append("0");
|
||||
}
|
||||
hexString.append(Integer.toHexString(0xFF & hashBytes[i]));
|
||||
}
|
||||
// Log.d("AntiTampering", String(hexString));
|
||||
return new String(hexString);
|
||||
}
|
||||
|
||||
}
|
87
src/android/com/cescit/integrity/AssetsIntegrity.java
Normal file
87
src/android/com/cescit/integrity/AssetsIntegrity.java
Normal file
@ -0,0 +1,87 @@
|
||||
package com.cescit.integrity;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.Base64;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
class AssetsIntegrity {
|
||||
|
||||
private static final String MESSAGE_DIGEST_ALGORITHM = "SHA-256";
|
||||
private static final String ASSETS_BASE_PATH = "";
|
||||
|
||||
private static final Map<String, String> hashList = Collections.unmodifiableMap(
|
||||
new HashMap<String, String>()
|
||||
);
|
||||
|
||||
public static JSONObject check(Context context) throws Exception {
|
||||
AssetManager assets = context.getAssets();
|
||||
for (Map.Entry<String, String> entry : hashList.entrySet()) {
|
||||
// byte[] fileNameDecode = Base64.decode(entry.getKey(), 0);
|
||||
// String fileName = new String(fileNameDecode, StandardCharsets.UTF_8);
|
||||
// Log.d("AntiTampering", fileName + " -> " + entry.getValue());
|
||||
String filePath = entry.getKey();
|
||||
InputStream file = assets.open(filePath);
|
||||
String hash = getFileHash(file);
|
||||
if (entry.getValue() == null || !entry.getValue().equals(hash)) {
|
||||
throw new Exception("Content of " + filePath + " has been tampered");
|
||||
}
|
||||
}
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("count", hashList.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
// 获取Res文件对应的hash值构造的String
|
||||
public static String getHashString(Context context) throws Exception{
|
||||
AssetManager assets = context.getAssets();
|
||||
String str = "";
|
||||
// 遍历对比文件hash
|
||||
for (Map.Entry<String, String> entry : hashList.entrySet()) {
|
||||
String fileName = entry.getKey();
|
||||
String presetHash = entry.getValue();
|
||||
if (!presetHash.equals("")) {
|
||||
str += "put(\"" + fileName + "\", \"" + presetHash + "\");";
|
||||
}
|
||||
}
|
||||
// 用默认字符编码解码字符串。
|
||||
byte[] bs = str.getBytes();
|
||||
str = new String(bs, StandardCharsets.UTF_8);
|
||||
return str;
|
||||
}
|
||||
|
||||
private static String getFileHash(InputStream file) throws IOException, NoSuchAlgorithmException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int nRead;
|
||||
byte[] data = new byte[16384];
|
||||
while ((nRead = file.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
buffer.flush();
|
||||
MessageDigest digest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM);
|
||||
byte[] hashBytes = digest.digest(buffer.toByteArray());
|
||||
StringBuffer hexString = new StringBuffer();
|
||||
for (int i = 0; i < hashBytes.length; i++) {
|
||||
if ((0xff & hashBytes[i]) < 0x10) {
|
||||
hexString.append("0");
|
||||
}
|
||||
hexString.append(Integer.toHexString(0xFF & hashBytes[i]));
|
||||
}
|
||||
// Log.d("AntiTampering", String(hexString));
|
||||
return new String(hexString);
|
||||
}
|
||||
|
||||
}
|
98
src/android/com/cescit/integrity/CescitIntegrity.java
Normal file
98
src/android/com/cescit/integrity/CescitIntegrity.java
Normal file
@ -0,0 +1,98 @@
|
||||
package com.cescit.integrity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class CescitIntegrity extends CordovaPlugin {
|
||||
|
||||
private Activity activity;
|
||||
private Context context;
|
||||
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
activity = cordova.getActivity();
|
||||
context = activity.getApplicationContext();
|
||||
checkAndStopExecution();
|
||||
super.initialize(cordova, webView);
|
||||
}
|
||||
|
||||
private void checkAndStopExecution() {
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
@Override
|
||||
public void run () {
|
||||
try {
|
||||
String ret = HttpUtil.getHttpRequestData((String) Config.getConfig("APK_HASH_URL"));
|
||||
JSONObject obj = new JSONObject(ret);
|
||||
if(obj.getBoolean("ApkIntegrity")) {
|
||||
ApkIntegrity.check(context);
|
||||
}
|
||||
if(obj.getBoolean("ResIntegrity")) {
|
||||
ResIntegrity.check(context);
|
||||
}
|
||||
if(obj.getBoolean("AssetsIntegrity")) {
|
||||
AssetsIntegrity.check(context);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new TamperingException("Anti-Tampering check failed");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
if ("verify".equals(action)) {
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
@Override
|
||||
public void run () {
|
||||
PluginResult result;
|
||||
try {
|
||||
// DebugDetection.check(activity.getPackageName());
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("assets", AssetsIntegrity.check(context));
|
||||
response.put("res", ResIntegrity.check(context));
|
||||
response.put("apk", ApkIntegrity.check(context));
|
||||
result = new PluginResult(PluginResult.Status.OK, response);
|
||||
} catch (Exception e) {
|
||||
result = new PluginResult(PluginResult.Status.ERROR, e.toString());
|
||||
}
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("getList".equals(action)) {
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
@Override
|
||||
public void run () {
|
||||
PluginResult result;
|
||||
try {
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("assets", AssetsIntegrity.getHashString(context));
|
||||
response.put("res", ResIntegrity.getHashString(context));
|
||||
response.put("apk", ApkIntegrity.getHashString(context));
|
||||
result = new PluginResult(PluginResult.Status.OK, response);
|
||||
} catch (Exception e) {
|
||||
result = new PluginResult(PluginResult.Status.ERROR, e.toString());
|
||||
}
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
20
src/android/com/cescit/integrity/Config.java
Normal file
20
src/android/com/cescit/integrity/Config.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.cescit.integrity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class Config {
|
||||
private static final String APK_HASH_URL = "http://webf.cewater.com.cn/apk-hash/jm.json";
|
||||
|
||||
// 获取配置
|
||||
public static <T> T getConfig(String key) throws Exception{
|
||||
// res资源(路径:文件hash)键值对
|
||||
Map<String, T> config = new HashMap<String,T>();
|
||||
config.put("APK_HASH_URL", (T) APK_HASH_URL);
|
||||
T result = null;
|
||||
for(String configKey : config.keySet()){
|
||||
result = config.get(configKey);
|
||||
}
|
||||
return (T) result;
|
||||
}
|
||||
}
|
28
src/android/com/cescit/integrity/DebugDetection.java
Normal file
28
src/android/com/cescit/integrity/DebugDetection.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.cescit.integrity;
|
||||
|
||||
import android.os.Debug;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
|
||||
class DebugDetection {
|
||||
|
||||
public static void check(String packageName) throws Exception {
|
||||
if (hasDebuggerAttached()) {
|
||||
throw new Exception("Debugger attached");
|
||||
} else if (getDebugField(packageName)) {
|
||||
throw new Exception("App running in Debug mode");
|
||||
}
|
||||
}
|
||||
|
||||
private static Boolean getDebugField(String packageName) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
|
||||
Class<?> buildConfigClass = Class.forName(packageName.concat(".BuildConfig"));
|
||||
Field debugField = buildConfigClass.getField("DEBUG");
|
||||
return debugField.getBoolean(null);
|
||||
}
|
||||
|
||||
private static Boolean hasDebuggerAttached() {
|
||||
return Debug.isDebuggerConnected() || Debug.waitingForDebugger();
|
||||
}
|
||||
|
||||
}
|
58
src/android/com/cescit/integrity/HttpUtil.java
Normal file
58
src/android/com/cescit/integrity/HttpUtil.java
Normal file
@ -0,0 +1,58 @@
|
||||
package com.cescit.integrity;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class HttpUtil {
|
||||
|
||||
public static String getHttpRequestData(String urlPath) {
|
||||
|
||||
// 首先抓取异常并处理
|
||||
String returnString = "";
|
||||
try{
|
||||
// 代码实现以GET请求方式为主,POST跳过
|
||||
/** 1 GET方式请求数据 start*/
|
||||
|
||||
// 1 创建URL对象,接收用户传递访问地址对象链接
|
||||
URL url = new URL(urlPath);
|
||||
|
||||
// 2 打开用户传递URL参数地址
|
||||
HttpURLConnection connect = (HttpURLConnection) url.openConnection();
|
||||
|
||||
// 3 设置HTTP请求的一些参数信息
|
||||
connect.setRequestMethod("GET"); // 参数必须大写
|
||||
connect.connect();
|
||||
|
||||
// 4 获取URL请求到的数据,并创建数据流接收
|
||||
InputStream isString = connect.getInputStream();
|
||||
|
||||
// 5 构建一个字符流缓冲对象,承载URL读取到的数据
|
||||
BufferedReader isRead = new BufferedReader(new InputStreamReader(isString));
|
||||
|
||||
// 6 输出打印获取到的文件流
|
||||
String str = "";
|
||||
while ((str = isRead.readLine()) != null) {
|
||||
str = new String(str.getBytes(),"UTF-8"); //解决中文乱码问题
|
||||
// System.out.println("文件解析打印:");
|
||||
// System.out.println(str);
|
||||
returnString += str;
|
||||
}
|
||||
|
||||
// 7 关闭流
|
||||
isString.close();
|
||||
connect.disconnect();
|
||||
|
||||
// 8 JSON转List对象
|
||||
// do somthings
|
||||
|
||||
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return returnString;
|
||||
}
|
||||
}
|
118
src/android/com/cescit/integrity/ResIntegrity.java
Normal file
118
src/android/com/cescit/integrity/ResIntegrity.java
Normal file
@ -0,0 +1,118 @@
|
||||
package com.cescit.integrity;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import android.content.Context;
|
||||
import java.io.File;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.regex.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
// apk res资源完整性检验
|
||||
class ResIntegrity {
|
||||
|
||||
private static final String MESSAGE_DIGEST_ALGORITHM = "SHA-256";
|
||||
private static final String ASSETS_BASE_PATH = "";
|
||||
|
||||
// Collections.unmodifiableMap 使得返回的内容只能只读访问
|
||||
private static final Map<String, String> hashList = Collections.unmodifiableMap(
|
||||
new HashMap<String, String>()
|
||||
);
|
||||
|
||||
public static JSONObject check(Context context) throws Exception {
|
||||
JSONObject result = new JSONObject();
|
||||
Map<String, String> resHashList = getResHashMap(context);
|
||||
// 遍历对比文件hash
|
||||
for (Map.Entry<String, String> entry : hashList.entrySet()) {
|
||||
// byte[] fileNameDecode = Base64.decode(entry.getKey(), 0);
|
||||
// String fileName = new String(fileNameDecode, StandardCharsets.UTF_8);
|
||||
String fileName = entry.getKey();
|
||||
String presetHash = entry.getValue();
|
||||
String filePath = ASSETS_BASE_PATH.concat(fileName);
|
||||
String nowHash = resHashList.get(filePath);
|
||||
if (presetHash == null || !presetHash.equals(nowHash)) {
|
||||
throw new Exception("Content of " + filePath + " has been tampered");
|
||||
}
|
||||
}
|
||||
result.put("res conunt", hashList.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
// 获取Res文件对应的hash值
|
||||
public static Map getResHashMap(Context context) throws Exception{
|
||||
File file = new File(context.getPackageCodePath());
|
||||
ZipFile zf = new ZipFile(file);
|
||||
// res资源(路径:文件)键值对
|
||||
Map<String, ZipEntry> resList = new HashMap<String,ZipEntry>();
|
||||
// res资源(路径:文件hash)键值对
|
||||
Map<String, String> resHashList = new HashMap<String,String>();
|
||||
// 提取出apk里的res文件资源
|
||||
Enumeration<?> entries = zf.entries();
|
||||
while(entries.hasMoreElements()) {
|
||||
ZipEntry zipEntry = (ZipEntry) entries.nextElement();
|
||||
String pattern = "^res\\/.*";
|
||||
boolean isMatch = Pattern.matches(pattern, zipEntry.getName());
|
||||
if(isMatch) {
|
||||
resList.put(zipEntry.getName(), zipEntry);
|
||||
}
|
||||
}
|
||||
// 获取文件hash
|
||||
for (Map.Entry<String, ZipEntry> resListItem : resList.entrySet()) {
|
||||
String key = resListItem.getKey();
|
||||
ZipEntry fileEntry = resListItem.getValue();
|
||||
InputStream fileSteam= zf.getInputStream(fileEntry);
|
||||
String fileHash = getFileHash(fileSteam);
|
||||
resHashList.put(key, fileHash);
|
||||
}
|
||||
return resHashList;
|
||||
}
|
||||
|
||||
// 获取Res文件对应的hash值构造的String
|
||||
public static String getHashString(Context context) throws Exception{
|
||||
Map<String, String> resHashList = getResHashMap(context);
|
||||
String str = "";
|
||||
// 遍历对比文件hash
|
||||
for (Map.Entry<String, String> entry : resHashList.entrySet()) {
|
||||
String fileName = entry.getKey();
|
||||
String presetHash = entry.getValue();
|
||||
if (!presetHash.equals("")) {
|
||||
str += "put(\"" + fileName + "\", \"" + presetHash + "\");";
|
||||
}
|
||||
}
|
||||
// 用默认字符编码解码字符串。
|
||||
byte[] bs = str.getBytes();
|
||||
str = new String(bs, StandardCharsets.UTF_8);
|
||||
return str;
|
||||
}
|
||||
|
||||
private static String getFileHash(InputStream file) throws IOException, NoSuchAlgorithmException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int nRead;
|
||||
byte[] data = new byte[16384];
|
||||
while ((nRead = file.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
buffer.flush();
|
||||
MessageDigest digest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM);
|
||||
byte[] hashBytes = digest.digest(buffer.toByteArray());
|
||||
StringBuffer hexString = new StringBuffer();
|
||||
for (int i = 0; i < hashBytes.length; i++) {
|
||||
if ((0xff & hashBytes[i]) < 0x10) {
|
||||
hexString.append("0");
|
||||
}
|
||||
hexString.append(Integer.toHexString(0xFF & hashBytes[i]));
|
||||
}
|
||||
// Log.d("AntiTampering", String(hexString));
|
||||
return new String(hexString);
|
||||
}
|
||||
|
||||
}
|
9
src/android/com/cescit/integrity/TamperingException.java
Normal file
9
src/android/com/cescit/integrity/TamperingException.java
Normal file
@ -0,0 +1,9 @@
|
||||
package com.cescit.integrity;
|
||||
|
||||
class TamperingException extends SecurityException {
|
||||
|
||||
public TamperingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
22
www/AntiTampering.js
Normal file
22
www/AntiTampering.js
Normal file
@ -0,0 +1,22 @@
|
||||
var exec = require('cordova/exec');
|
||||
|
||||
function AntiTampering () {}
|
||||
|
||||
AntiTampering.prototype.verify = function (successCallback, errorCallback) {
|
||||
exec(successCallback,
|
||||
errorCallback,
|
||||
'CescitIntegrity',
|
||||
'verify',
|
||||
[]);
|
||||
};
|
||||
|
||||
AntiTampering.prototype.getList = function (successCallback, errorCallback) {
|
||||
exec(successCallback,
|
||||
errorCallback,
|
||||
'CescitIntegrity',
|
||||
'getList',
|
||||
[]);
|
||||
};
|
||||
|
||||
|
||||
module.exports = new AntiTampering();
|
Loading…
Reference in New Issue
Block a user