1*b7c941bbSAndroid Build Coastguard Worker /* 2*b7c941bbSAndroid Build Coastguard Worker * Copyright (C) 2008 The Android Open Source Project 3*b7c941bbSAndroid Build Coastguard Worker * 4*b7c941bbSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*b7c941bbSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*b7c941bbSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*b7c941bbSAndroid Build Coastguard Worker * 8*b7c941bbSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*b7c941bbSAndroid Build Coastguard Worker * 10*b7c941bbSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*b7c941bbSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*b7c941bbSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*b7c941bbSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*b7c941bbSAndroid Build Coastguard Worker * limitations under the License. 15*b7c941bbSAndroid Build Coastguard Worker */ 16*b7c941bbSAndroid Build Coastguard Worker 17*b7c941bbSAndroid Build Coastguard Worker package dxconvext; 18*b7c941bbSAndroid Build Coastguard Worker 19*b7c941bbSAndroid Build Coastguard Worker import dxconvext.util.FileUtils; 20*b7c941bbSAndroid Build Coastguard Worker 21*b7c941bbSAndroid Build Coastguard Worker import java.io.BufferedOutputStream; 22*b7c941bbSAndroid Build Coastguard Worker import java.io.BufferedReader; 23*b7c941bbSAndroid Build Coastguard Worker import java.io.ByteArrayInputStream; 24*b7c941bbSAndroid Build Coastguard Worker import java.io.ByteArrayOutputStream; 25*b7c941bbSAndroid Build Coastguard Worker import java.io.File; 26*b7c941bbSAndroid Build Coastguard Worker import java.io.FileInputStream; 27*b7c941bbSAndroid Build Coastguard Worker import java.io.FileNotFoundException; 28*b7c941bbSAndroid Build Coastguard Worker import java.io.FileOutputStream; 29*b7c941bbSAndroid Build Coastguard Worker import java.io.IOException; 30*b7c941bbSAndroid Build Coastguard Worker import java.io.InputStreamReader; 31*b7c941bbSAndroid Build Coastguard Worker import java.io.OutputStream; 32*b7c941bbSAndroid Build Coastguard Worker import java.io.Reader; 33*b7c941bbSAndroid Build Coastguard Worker import java.io.UnsupportedEncodingException; 34*b7c941bbSAndroid Build Coastguard Worker import java.security.DigestException; 35*b7c941bbSAndroid Build Coastguard Worker import java.security.MessageDigest; 36*b7c941bbSAndroid Build Coastguard Worker import java.security.NoSuchAlgorithmException; 37*b7c941bbSAndroid Build Coastguard Worker import java.util.zip.Adler32; 38*b7c941bbSAndroid Build Coastguard Worker 39*b7c941bbSAndroid Build Coastguard Worker public class ClassFileAssembler { 40*b7c941bbSAndroid Build Coastguard Worker 41*b7c941bbSAndroid Build Coastguard Worker /** 42*b7c941bbSAndroid Build Coastguard Worker * @param args 43*b7c941bbSAndroid Build Coastguard Worker */ main(String[] args)44*b7c941bbSAndroid Build Coastguard Worker public static void main(String[] args) { 45*b7c941bbSAndroid Build Coastguard Worker ClassFileAssembler cfa = new ClassFileAssembler(); 46*b7c941bbSAndroid Build Coastguard Worker cfa.run(args); 47*b7c941bbSAndroid Build Coastguard Worker } 48*b7c941bbSAndroid Build Coastguard Worker run(String[] args)49*b7c941bbSAndroid Build Coastguard Worker private void run(String[] args) { 50*b7c941bbSAndroid Build Coastguard Worker // this class can be used to generate .class files that are somehow 51*b7c941bbSAndroid Build Coastguard Worker // damaged in order to test the dalvik vm verifier. 52*b7c941bbSAndroid Build Coastguard Worker // The input is a .cfh (class file hex) file. 53*b7c941bbSAndroid Build Coastguard Worker // The output is a java vm .class file. 54*b7c941bbSAndroid Build Coastguard Worker // The .cfh files can be generated as follows: 55*b7c941bbSAndroid Build Coastguard Worker // 1. create the initial .cfh file from an existing .class files by using 56*b7c941bbSAndroid Build Coastguard Worker // the ClassFileParser 57*b7c941bbSAndroid Build Coastguard Worker // 2. modify some bytes to damage the structure of the .class file in a 58*b7c941bbSAndroid Build Coastguard Worker // way that would not be possible with e.g. jasmin (otherwise you are 59*b7c941bbSAndroid Build Coastguard Worker // better off using jasmin). 60*b7c941bbSAndroid Build Coastguard Worker // Uncomment the original bytes, and write "MOD:" meaning a modified 61*b7c941bbSAndroid Build Coastguard Worker // entry (with the original commented out) 62*b7c941bbSAndroid Build Coastguard Worker // 63*b7c941bbSAndroid Build Coastguard Worker // Use the ClassFileAssembler to generate the .class file. 64*b7c941bbSAndroid Build Coastguard Worker // this class here simply takes all non-comment lines from the .cfh 65*b7c941bbSAndroid Build Coastguard Worker // file, parses them as hex values and writes the bytes to the class file 66*b7c941bbSAndroid Build Coastguard Worker File cfhF = new File(args[0]); 67*b7c941bbSAndroid Build Coastguard Worker if (!cfhF.getName().endsWith(".cfh") && 68*b7c941bbSAndroid Build Coastguard Worker !cfhF.getName().endsWith(".dfh")) { 69*b7c941bbSAndroid Build Coastguard Worker System.out.println("file must be a .cfh or .dfh file, and its filename end with .cfh or .dfh"); 70*b7c941bbSAndroid Build Coastguard Worker return; 71*b7c941bbSAndroid Build Coastguard Worker } 72*b7c941bbSAndroid Build Coastguard Worker 73*b7c941bbSAndroid Build Coastguard Worker String outBase = args[1]; 74*b7c941bbSAndroid Build Coastguard Worker 75*b7c941bbSAndroid Build Coastguard Worker boolean isDex = cfhF.getName().endsWith(".dfh"); 76*b7c941bbSAndroid Build Coastguard Worker 77*b7c941bbSAndroid Build Coastguard Worker byte[] cfhbytes = FileUtils.readFile(cfhF); 78*b7c941bbSAndroid Build Coastguard Worker ByteArrayInputStream bais = new ByteArrayInputStream(cfhbytes); 79*b7c941bbSAndroid Build Coastguard Worker // encoding should not matter, since we are skipping comment lines and parsing 80*b7c941bbSAndroid Build Coastguard Worker try { 81*b7c941bbSAndroid Build Coastguard Worker // get the package name 82*b7c941bbSAndroid Build Coastguard Worker BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(cfhF))); 83*b7c941bbSAndroid Build Coastguard Worker String firstLine = br.readLine(); 84*b7c941bbSAndroid Build Coastguard Worker br.close(); 85*b7c941bbSAndroid Build Coastguard Worker String classHdr = "//@class:"; 86*b7c941bbSAndroid Build Coastguard Worker String dexHdr = "// Processing '"; 87*b7c941bbSAndroid Build Coastguard Worker String hdr; 88*b7c941bbSAndroid Build Coastguard Worker if(isDex) 89*b7c941bbSAndroid Build Coastguard Worker hdr = dexHdr; 90*b7c941bbSAndroid Build Coastguard Worker else 91*b7c941bbSAndroid Build Coastguard Worker hdr = classHdr; 92*b7c941bbSAndroid Build Coastguard Worker 93*b7c941bbSAndroid Build Coastguard Worker if (!firstLine.startsWith(hdr)) throw new RuntimeException("wrong format:"+firstLine +" isDex=" + isDex); 94*b7c941bbSAndroid Build Coastguard Worker String tFile; 95*b7c941bbSAndroid Build Coastguard Worker if(isDex) { 96*b7c941bbSAndroid Build Coastguard Worker tFile = outBase + "/classes.dex"; 97*b7c941bbSAndroid Build Coastguard Worker } else { 98*b7c941bbSAndroid Build Coastguard Worker String classO = firstLine.substring(hdr.length()).trim(); 99*b7c941bbSAndroid Build Coastguard Worker tFile = outBase +"/"+classO+".class"; 100*b7c941bbSAndroid Build Coastguard Worker } 101*b7c941bbSAndroid Build Coastguard Worker File outFile = new File(tFile); 102*b7c941bbSAndroid Build Coastguard Worker System.out.println("outfile:" + outFile); 103*b7c941bbSAndroid Build Coastguard Worker String mkdir = tFile.substring(0, tFile.lastIndexOf("/")); 104*b7c941bbSAndroid Build Coastguard Worker new File(mkdir).mkdirs(); 105*b7c941bbSAndroid Build Coastguard Worker 106*b7c941bbSAndroid Build Coastguard Worker Reader r = new InputStreamReader(bais,"utf-8"); 107*b7c941bbSAndroid Build Coastguard Worker OutputStream os = new FileOutputStream(outFile); 108*b7c941bbSAndroid Build Coastguard Worker BufferedOutputStream bos = new BufferedOutputStream(os); 109*b7c941bbSAndroid Build Coastguard Worker writeClassFile(r, bos, isDex); 110*b7c941bbSAndroid Build Coastguard Worker bos.close(); 111*b7c941bbSAndroid Build Coastguard Worker } catch (UnsupportedEncodingException e) { 112*b7c941bbSAndroid Build Coastguard Worker throw new RuntimeException("problem while parsing .dfh or .cfh file: "+cfhF.getAbsolutePath(), e); 113*b7c941bbSAndroid Build Coastguard Worker } catch (FileNotFoundException e) { 114*b7c941bbSAndroid Build Coastguard Worker throw new RuntimeException("problem while parsing .dfh or .cfh file: "+cfhF.getAbsolutePath(), e); 115*b7c941bbSAndroid Build Coastguard Worker } catch (IOException e) { 116*b7c941bbSAndroid Build Coastguard Worker throw new RuntimeException("problem while parsing .dfh or .cfh file: "+cfhF.getAbsolutePath(), e); 117*b7c941bbSAndroid Build Coastguard Worker } 118*b7c941bbSAndroid Build Coastguard Worker } 119*b7c941bbSAndroid Build Coastguard Worker 120*b7c941bbSAndroid Build Coastguard Worker /** 121*b7c941bbSAndroid Build Coastguard Worker * Calculates the signature for the <code>.dex</code> file in the 122*b7c941bbSAndroid Build Coastguard Worker * given array, and modify the array to contain it. 123*b7c941bbSAndroid Build Coastguard Worker * 124*b7c941bbSAndroid Build Coastguard Worker * Originally from com.android.dx.dex.file.DexFile. 125*b7c941bbSAndroid Build Coastguard Worker * 126*b7c941bbSAndroid Build Coastguard Worker * @param bytes non-null; the bytes of the file 127*b7c941bbSAndroid Build Coastguard Worker */ calcSignature(byte[] bytes)128*b7c941bbSAndroid Build Coastguard Worker private void calcSignature(byte[] bytes) { 129*b7c941bbSAndroid Build Coastguard Worker MessageDigest md; 130*b7c941bbSAndroid Build Coastguard Worker 131*b7c941bbSAndroid Build Coastguard Worker try { 132*b7c941bbSAndroid Build Coastguard Worker md = MessageDigest.getInstance("SHA-1"); 133*b7c941bbSAndroid Build Coastguard Worker } catch (NoSuchAlgorithmException ex) { 134*b7c941bbSAndroid Build Coastguard Worker throw new RuntimeException(ex); 135*b7c941bbSAndroid Build Coastguard Worker } 136*b7c941bbSAndroid Build Coastguard Worker 137*b7c941bbSAndroid Build Coastguard Worker md.update(bytes, 32, bytes.length - 32); 138*b7c941bbSAndroid Build Coastguard Worker 139*b7c941bbSAndroid Build Coastguard Worker try { 140*b7c941bbSAndroid Build Coastguard Worker int amt = md.digest(bytes, 12, 20); 141*b7c941bbSAndroid Build Coastguard Worker if (amt != 20) { 142*b7c941bbSAndroid Build Coastguard Worker throw new RuntimeException("unexpected digest write: " + amt + 143*b7c941bbSAndroid Build Coastguard Worker " bytes"); 144*b7c941bbSAndroid Build Coastguard Worker } 145*b7c941bbSAndroid Build Coastguard Worker } catch (DigestException ex) { 146*b7c941bbSAndroid Build Coastguard Worker throw new RuntimeException(ex); 147*b7c941bbSAndroid Build Coastguard Worker } 148*b7c941bbSAndroid Build Coastguard Worker } 149*b7c941bbSAndroid Build Coastguard Worker 150*b7c941bbSAndroid Build Coastguard Worker /** 151*b7c941bbSAndroid Build Coastguard Worker * Calculates the checksum for the <code>.dex</code> file in the 152*b7c941bbSAndroid Build Coastguard Worker * given array, and modify the array to contain it. 153*b7c941bbSAndroid Build Coastguard Worker * 154*b7c941bbSAndroid Build Coastguard Worker * Originally from com.android.dx.dex.file.DexFile. 155*b7c941bbSAndroid Build Coastguard Worker * 156*b7c941bbSAndroid Build Coastguard Worker * @param bytes non-null; the bytes of the file 157*b7c941bbSAndroid Build Coastguard Worker */ calcChecksum(byte[] bytes)158*b7c941bbSAndroid Build Coastguard Worker private void calcChecksum(byte[] bytes) { 159*b7c941bbSAndroid Build Coastguard Worker Adler32 a32 = new Adler32(); 160*b7c941bbSAndroid Build Coastguard Worker 161*b7c941bbSAndroid Build Coastguard Worker a32.update(bytes, 12, bytes.length - 12); 162*b7c941bbSAndroid Build Coastguard Worker 163*b7c941bbSAndroid Build Coastguard Worker int sum = (int) a32.getValue(); 164*b7c941bbSAndroid Build Coastguard Worker 165*b7c941bbSAndroid Build Coastguard Worker bytes[8] = (byte) sum; 166*b7c941bbSAndroid Build Coastguard Worker bytes[9] = (byte) (sum >> 8); 167*b7c941bbSAndroid Build Coastguard Worker bytes[10] = (byte) (sum >> 16); 168*b7c941bbSAndroid Build Coastguard Worker bytes[11] = (byte) (sum >> 24); 169*b7c941bbSAndroid Build Coastguard Worker } 170*b7c941bbSAndroid Build Coastguard Worker writeClassFile(Reader r, OutputStream rOs, boolean isDex)171*b7c941bbSAndroid Build Coastguard Worker public void writeClassFile(Reader r, OutputStream rOs, boolean isDex) { 172*b7c941bbSAndroid Build Coastguard Worker ByteArrayOutputStream baos = new ByteArrayOutputStream(8192); 173*b7c941bbSAndroid Build Coastguard Worker BufferedReader br = new BufferedReader(r); 174*b7c941bbSAndroid Build Coastguard Worker String line; 175*b7c941bbSAndroid Build Coastguard Worker String secondLine = null; 176*b7c941bbSAndroid Build Coastguard Worker int lineCnt = 0; 177*b7c941bbSAndroid Build Coastguard Worker try { 178*b7c941bbSAndroid Build Coastguard Worker while ((line = br.readLine()) != null) { 179*b7c941bbSAndroid Build Coastguard Worker if (isDex && lineCnt++ == 1) { 180*b7c941bbSAndroid Build Coastguard Worker secondLine = line; 181*b7c941bbSAndroid Build Coastguard Worker } 182*b7c941bbSAndroid Build Coastguard Worker // skip it if it is a comment 183*b7c941bbSAndroid Build Coastguard Worker if (!line.trim().startsWith("//")) { 184*b7c941bbSAndroid Build Coastguard Worker // we have a row like " ae 08 21 ff" etc. 185*b7c941bbSAndroid Build Coastguard Worker String[] parts = line.split("\\s+"); 186*b7c941bbSAndroid Build Coastguard Worker for (int i = 0; i < parts.length; i++) { 187*b7c941bbSAndroid Build Coastguard Worker String part = parts[i].trim(); 188*b7c941bbSAndroid Build Coastguard Worker if (!part.equals("")) { 189*b7c941bbSAndroid Build Coastguard Worker int res = Integer.parseInt(part, 16); 190*b7c941bbSAndroid Build Coastguard Worker baos.write(res); 191*b7c941bbSAndroid Build Coastguard Worker } 192*b7c941bbSAndroid Build Coastguard Worker } 193*b7c941bbSAndroid Build Coastguard Worker } 194*b7c941bbSAndroid Build Coastguard Worker } 195*b7c941bbSAndroid Build Coastguard Worker 196*b7c941bbSAndroid Build Coastguard Worker // now for dex, update the checksum and the signature. 197*b7c941bbSAndroid Build Coastguard Worker // special case: 198*b7c941bbSAndroid Build Coastguard Worker // for two tests (currently T_f1_9.dfh and T_f1_10.dfh), we need 199*b7c941bbSAndroid Build Coastguard Worker // to keep the checksum or the signature, respectively. 200*b7c941bbSAndroid Build Coastguard Worker byte[] outBytes = baos.toByteArray(); 201*b7c941bbSAndroid Build Coastguard Worker if (isDex) { 202*b7c941bbSAndroid Build Coastguard Worker boolean leaveChecksum = secondLine.contains("//@leaveChecksum"); 203*b7c941bbSAndroid Build Coastguard Worker boolean leaveSignature= secondLine.contains("//@leaveSignature"); 204*b7c941bbSAndroid Build Coastguard Worker // update checksum and signature for dex file 205*b7c941bbSAndroid Build Coastguard Worker if(!leaveSignature) 206*b7c941bbSAndroid Build Coastguard Worker calcSignature(outBytes); 207*b7c941bbSAndroid Build Coastguard Worker if(!leaveChecksum) 208*b7c941bbSAndroid Build Coastguard Worker calcChecksum(outBytes); 209*b7c941bbSAndroid Build Coastguard Worker } 210*b7c941bbSAndroid Build Coastguard Worker rOs.write(outBytes); 211*b7c941bbSAndroid Build Coastguard Worker rOs.close(); 212*b7c941bbSAndroid Build Coastguard Worker } catch (IOException e) { 213*b7c941bbSAndroid Build Coastguard Worker throw new RuntimeException("problem while writing file",e); 214*b7c941bbSAndroid Build Coastguard Worker } 215*b7c941bbSAndroid Build Coastguard Worker } 216*b7c941bbSAndroid Build Coastguard Worker 217*b7c941bbSAndroid Build Coastguard Worker } 218