001 /* 002 * =========================================================================================== 003 * = COPYRIGHT 004 * PAX Computer Technology (Shenzhen) Co., Ltd. PROPRIETARY INFORMATION 005 * This software is supplied under the terms of a license agreement or nondisclosure 006 * agreement with PAX Computer Technology (Shenzhen) Co., Ltd. and may not be copied or 007 * disclosed except in accordance with the terms in that agreement. 008 * Copyright (C) 2017-2023 PAX Computer Technology (Shenzhen) Co., Ltd. All rights reserved. 009 * Description: // Detail description about the function of this module, 010 * // interfaces with the other modules, and dependencies. 011 * Revision History: 012 * Date Author Action 013 * 2017/04/01 PAX Create/Add/Modify/Delete 014 * =========================================================================================== 015 */ 016 017package com.pax.neptunelite.api; 018 019import android.content.Context; 020import android.text.TextUtils; 021 022import com.pax.dal.IDAL; 023import com.pax.dal.proxy.IDALProxy; 024 025import java.io.BufferedInputStream; 026import java.io.File; 027import java.io.FileInputStream; 028import java.io.FileOutputStream; 029import java.io.IOException; 030import java.io.InputStream; 031import java.lang.reflect.Constructor; 032import java.lang.reflect.Field; 033import java.security.MessageDigest; 034import java.security.NoSuchAlgorithmException; 035import android.os.Build; 036import android.os.AsyncTask; 037import dalvik.system.DexClassLoader; 038import android.util.Log; 039 040public class NeptuneLiteUser { 041 042 private static NeptuneLiteUser neptuneLiteUser; 043 private IDAL dal; 044 045 private static final String DALPROXY_CLASS_NAME = "com.pax.dal.impl.DALProxy"; 046 private static final String FILE_NAME = "nepcore.dex"; 047 048 private static final String HASH256_OF_NEP_CORE_DEX = "d4bcc80ab13aac56bb16f0d85a3785262d4fa51fcefe4a75189511b36d683112"; 049 050 private DexClassLoader dexClassLoader = null; 051 052 private NeptuneLiteUser() { 053 054 } 055 056 057 public static synchronized NeptuneLiteUser getInstance() { 058 if (neptuneLiteUser == null) { 059 neptuneLiteUser = new NeptuneLiteUser(); 060 } 061 062 return neptuneLiteUser; 063 } 064 065 /** 066 * <div class="en">get DAL interface</div> <div class="zh">获取DAL句柄</div> 067 * 068 * @param context 069 * @return 070 * @throws Exception 071 */ 072 public synchronized IDAL getDal(Context context) throws Exception { 073 // NeptuneLiteProvider neptune = Utils.checkNeptuneLiteService(context); 074 // 如果已经加载过dex, 就直接返回IDAL接口 075 if (dal == null) { 076 dal = loadDalDex(context, FILE_NAME); 077 } 078 return dal; 079 } 080 081 /** 082 * <div class="zh">在多线程安全下获取的IDAL。</div> <div class="en">Get IDAL under multi-process security.</div> 083 * @param context context 084 * @return IDAL 085 * @throws Exception Exception 086 * @since V3.22.00 087 */ 088 public synchronized IDAL getDalWithProcessSafe(Context context) throws Exception { 089 if (dal == null) { 090 int pid = android.os.Process.myPid(); 091 String dexFileName = pid + ".dex"; 092 dal = loadDalDex(context, dexFileName); 093 cleanRuntimeFiles(context); 094 } 095 return dal; 096 } 097 098 private void cleanRuntimeFiles(Context context) { 099 new AsyncTask<Void, Void, Void>() { 100 @Override 101 protected Void doInBackground(Void... voids) { 102 try { 103 File targetDir = Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? 104 new File(context.getApplicationInfo().dataDir, "app_dex") : 105 new File(context.getFilesDir(), "oat"); 106 cleanDex(targetDir); 107 } catch (Exception e) { 108 Log.e("Neptune","CatchException: "+e); 109 } 110 return null; 111 } 112 }.execute(); 113 } 114 115 private static void cleanDex(File dir) { 116 if (!dir.exists()) return; 117 File[] files = dir.listFiles(); 118 if (files != null) { 119 for (File file : files) { 120 if (file.isDirectory()) { 121 cleanDex(file); 122 } else { 123 String name = file.getName(); 124 long fileTime = System.currentTimeMillis() - file.lastModified(); 125 if (name.matches("\\d+\\.(dex|vdex|odex|dex\\.cur\\.prof)") && (fileTime > (24 * 60 * 60 * 1000L))) { 126 file.delete(); 127 } 128 } 129 } 130 } 131 } 132 133 /** 134 * 动态加载设备操作实现 135 * 136 * @param context 137 * @return 138 * @throws Exception 139 */ 140 private IDAL loadDalDex(Context context, String dexFileName) throws Exception { 141 if (TextUtils.isEmpty(dexFileName)){ 142 throw new Exception("LOAD DAL ERR"); 143 } 144 File file = null; 145 try { 146 file = new File(context.getFilesDir().getAbsolutePath(), dexFileName); 147 if (!writeNepcoreFile(file)){ 148 throw new Exception("LOAD DAL ERR"); 149 } 150 file.setReadOnly(); 151 // 动态加载 152 String dexPath = file.getAbsolutePath(); 153 String hash = getFileHexStrHash(new File(dexPath)); 154 if (hash != null && !hash.isEmpty()) { 155 if (!hash.equalsIgnoreCase(HASH256_OF_NEP_CORE_DEX)) { 156 throw new Exception("DEX INTEGRITY CHECK ERR"); 157 } 158 } else { 159 throw new Exception("GET FILE HASH ERR"); 160 } 161 162 String optimizedDirectoryPath = context.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath(); 163 // String libraryPath = context.getFilesDir().getParent() + File.separator + "lib/"; 164 // so路径 165 String libraryPath = context.getApplicationInfo().nativeLibraryDir; 166 dexClassLoader = new DexClassLoader(dexPath, optimizedDirectoryPath, libraryPath, context.getClassLoader()); 167 Class<?> glClass = dexClassLoader.loadClass(DALPROXY_CLASS_NAME); 168 Constructor<?> istructor = glClass.getConstructor(Context.class); 169 IDALProxy dalProxy = ((IDALProxy) istructor.newInstance(context)); 170 return dalProxy.getDal(); 171 } catch (Exception e) { 172 Log.e("Neptune","CatchException: "+e); 173 throw new Exception("LOAD DAL ERR"); 174 } finally { 175 // 删除临时文件 176 if (null != file && file.exists()) { 177 file.delete(); 178 } 179 } 180 } 181 182 private String getNepcoreFieldValue(Nepcore nepcore, String fieldName) { 183 Class<?> clazz = nepcore.getClass(); 184 Field[] fields = clazz.getDeclaredFields(); 185 for (Field field : fields) { 186 if (fieldName.equals(field.getName())) { 187 field.setAccessible(true); 188 try { 189 return (String) field.get(nepcore); 190 } catch (IllegalAccessException | IllegalArgumentException e) { 191 Log.e("Neptune","CatchException: "+e); 192 } 193 } 194 } 195 return null; 196 } 197 198 private boolean writeNepcoreFile(File file) { 199 FileOutputStream out = null; 200 try { 201 // 如果文件存在,则先删除 202 if (file == null) { 203 return false; 204 } 205 if (file.exists()) { 206 file.delete(); 207 } 208 out = new FileOutputStream(file); 209 Nepcore nepcore = new Nepcore(); 210 for (int i = 0; i < nepcore.count; i++) { 211 String dexData = getNepcoreFieldValue(nepcore, "nepcore" + i); 212 if (dexData != null) { 213 byte[] dex = strToBcd(dexData); 214 out.write(dex, 0, dex.length); 215 } 216 } 217 return true; 218 } catch (IOException e) { 219 Log.e("Neptune","CatchException: "+e); 220 return false; 221 } finally { 222 try { 223 if (out != null) { 224 out.flush(); 225 } 226 } catch (IOException e) { 227 Log.e("Neptune","CatchException: "+e); 228 } 229 230 try { 231 if (out != null) { 232 out.close(); 233 } 234 } catch (IOException e) { 235 Log.e("Neptune","CatchException: "+e); 236 } 237 } 238 } 239 240 byte[] strToBcd(String str) { 241 int len = str.length(); 242 int mod = len % 2; 243 if (mod != 0) { 244 str = str + "0"; 245 len = str.length(); 246 } 247 byte[] abt; 248 if (len >= 2) { 249 len = len / 2; 250 } 251 byte[] bbt = new byte[len]; 252 abt = str.getBytes(); 253 int j, k; 254 for (int p = 0; p < str.length() / 2; p++) { 255 if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) { 256 j = abt[2 * p] - 'a' + 0x0a; 257 } else if ((abt[2 * p] >= 'A') && (abt[2 * p] <= 'Z')) { 258 j = abt[2 * p] - 'A' + 0x0a; 259 } else { 260 j = abt[2 * p] - '0'; 261 } 262 263 if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) { 264 k = abt[2 * p + 1] - 'a' + 0x0a; 265 } else if ((abt[2 * p + 1] >= 'A') && (abt[2 * p + 1] <= 'Z')) { 266 k = abt[2 * p + 1] - 'A' + 0x0a; 267 } else { 268 k = abt[2 * p + 1] - '0'; 269 } 270 271 int a = (j << 4) + k; 272 byte b = (byte) a; 273 bbt[p] = b; 274 } 275 return bbt; 276 } 277 278 279 private static String getFileHexStrHash(File file) { 280 return byteArrayToHexStr(getFileByteHash(file)); 281 } 282 283 private static byte[] getFileByteHash(File file) { 284 if (file == null || !file.exists()) { 285 return null; 286 } 287 InputStream inputStream = null; 288 try { 289 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); 290 inputStream = new BufferedInputStream(new FileInputStream(file)); 291 byte[] temp = new byte[2048]; 292 int len; 293 while ((len = inputStream.read(temp)) != -1) { 294 messageDigest.update(temp, 0, len); 295 } 296 return messageDigest.digest(); 297 } catch (IOException | NoSuchAlgorithmException e) { 298 Log.e("Neptune","CatchException: "+e); 299 return null; 300 } finally { 301 if (inputStream != null) { 302 try { 303 inputStream.close(); 304 } catch (IOException e) { 305 Log.e("Neptune","CatchException: "+e); 306 } 307 } 308 } 309 } 310 311 private static String byteArrayToHexStr(byte[] byteArray) { 312 if (byteArray == null) { 313 return null; 314 } 315 char[] hexArray = "0123456789ABCDEF".toCharArray(); 316 char[] hexChars = new char[byteArray.length * 2]; 317 for (int j = 0; j < byteArray.length; j++) { 318 int v = byteArray[j] & 0xFF; 319 hexChars[j * 2] = hexArray[v >>> 4]; 320 hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 321 } 322 return new String(hexChars); 323 } 324 325}