Android中的Apk的加固(加壳)原理解析和实现
Android中的Apk的加固(加壳)原理解
析和实现
一、前言
今天又到周末了,憋了好久又要出博客了,今天来介绍一下Android中的如何对Apk进行加固的原理。现阶段。我们知道Android中的反编译工作越来越让人操作熟练,我们辛苦的开发出一个apk,结果被人反编译了,那心情真心不舒服。虽然我们混淆,做到native层,但是这都是治标不治本。反编译的技术在更新,那么保护Apk的技术就不能停止。现在网上有很多Apk加固的第三方平台,最有名的应当属于:爱加密和梆梆加固了。其实加固有些人认为很高深的技术,其实不然,说的简单点就是对源Apk进行加密,然后在套上一层壳即可,当然这里还有一些细节需要处理,这就是本文需要介绍的内容了。
二、原理解析
下面就来看一下Android中加壳的原理:
我们在加固的过程中需要三个对象:
1、需要加密的Apk(源Apk)
2、壳程序Apk(负责解密Apk工作)
3、加密工具(将源Apk进行加密和壳Dex合并成新的Dex)
主要步骤:
我们拿到需要加密的Apk和自己的壳程序Apk,然后用加密算法对源Apk进行加密在将壳Apk进行合并得到新的Dex文件,最后替换壳程序中的dex文件即可,得到新的Apk,那么这个新的Apk我们也叫作脱壳程序Apk.他已经不是一个完整意义上的Apk程序了,他的主要工作是:负责解密源Apk.然后加载Apk,让其正常运行起来。
在这个过程中我们可能需要了解的一个知识是:如何将源Apk和壳Apk进行合并成新的Dex 这里就需要了解Dex文件的格式了。下面就来简单介绍一下Dex文件的格式
主要来看一下Dex文件的头部信息,其实Dex文件和Class文件的格式分析原理都是一样的,他们都是有固定的格式,我们知道现在反编译的一些工具:
1、jd-gui:可以查看jar中的类,其实他就是解析class文件,只要了解class文件的格式就可以
2、dex2jar:将dex文件转化成jar,原理也是一样的,只要知道Dex文件的格式,能够解析出dex文件中的类信息就可以了
当然我们在分析这个文件的时候,最重要的还是头部信息,应该他是一个文件的开始部分,也是索引部分,内部信息很重要。
我们今天只要关注上面红色标记的三个部分:
1) checksum
文件校验码,使用alder32 算法校验文件除去maigc ,checksum 外余下的所有文件区域,用于检查文件错误。
2) signature
使用SHA-1 算法hash 除去magic ,checksum 和signature 外余下的所有文件区域,用于唯一识别本文件。
3) file_size
Dex 文件的大小。
为什么说我们只需要关注这三个字段呢?
因为我们需要将一个文件(加密之后的源Apk)写入到Dex中,那么我们肯定需要修改文件校验码(checksum).因为他是检查文件是否有错误。那么signature也是一样,也是唯一识别文件的算法。还有就是需要修改dex文件的大小。
不过这里还需要一个操作,就是标注一下我们加密的Apk的大小,因为我们在脱壳的时候,需要知道Apk的大小,才能正确的得到Apk。那么这个值放到哪呢?这个值直接放到文件的末尾就可以了。
所以总结一下我们需要做:修改Dex的三个文件头,将源Apk的大小追加到壳dex的末尾就可以了。
我们修改之后得到新的Dex文件样式如下:
那么我们知道原理了,下面就是代码实现了。所以这里有三个工程:
1、源程序项目(需要加密的Apk)
2、脱壳项目(解密源Apk和加载Apk)
3、对源Apk进行加密和脱壳项目的Dex的合并
三、项目案例
下面先来看一下源程序
1、需要加密的源程序Apk项目:ForceApkObj
需要一个Application类,这个到后面说为什么需要:
MyApplication.Java
[java] view plain copy
package com.example.forceapkobj;
import android.app.Application;
import android.util.Log;
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
Log.i("demo", "source apk onCreate:"+this);
}
}
就是打印一下onCreate方法。
MainActivity.Java
[java] view plain copy
package com.example.forceapkobj;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView content = new TextView(this);
content.setText("I am Source Apk");
content.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
Intent intent = new Intent(MainActivity.this, SubActivity.class);
startActivity(intent);
}});
setContentView(content);
Log.i("demo", "app:"+getApplicationContext());
}
}
也是打印一下内容。
2、加壳程序项目:DexShellTools
加壳程序其实就是一个Java工程,因为我们从上面的分析可以看到,他的工作就是加密源Apk,然后将其写入到脱壳Dex文件中,修改文件头,得到一个新的Dex文件即可。
看一下代码:
[java] view plain copy
package com.example.reforceapk;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.Adler32;
public class mymain {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
File payloadSrcFile = new File("force/ForceApkObj.apk"); //需要加壳的程序
System.out.println("apk size:"+payloadSrcFile.length());
File unShellDexFile = new File("force/ForceApkObj.dex"); //解客dex
byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));//以二进制形式读出apk,并进行加密处理//对源Apk进行加密操作
byte[] unShellDexArray = readFileBytes(unShellDexFile);//以二进制形式读出dex
int payloadLen = payloadArray.length;
int unShellDexLen = unShellDexArray.length;
int totalLen = payloadLen + unShellDexLen +4;//多出4字节是存放长度的。
byte[] newdex = new byte[totalLen]; // 申请了新的长度
//添加解壳代码
System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);//先拷贝dex 内容
//添加加密后的解壳数据
System.arraycopy(payloadArray, 0, newdex, unShellDexLen, payloadLen);//再在dex内容后面拷贝apk的内容
//添加解壳数据长度
System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4);//最后4为长度
//修改DEX file size文件头
fixFileSizeHeader(newdex);
//修改DEX SHA1 文件头
fixSHA1Header(newdex);
//修改DEX CheckSum文件头
fixCheckSumHeader(newdex);
String str = "force/classes.dex";
File file = new File(str);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream localFileOutputStream = new FileOutputStream(str);
localFileOutputStream.write(newdex);
localFileOutputStream.flush();
localFileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//直接返回数据,读者可以添加自己加密方法
private static byte[] encrpt(byte[] srcdata){
for(int i = 0;i srcdata[i] = (byte)(0xFF ^ srcdata[i]); } return srcdata; } /** * 修改dex头,CheckSum 校验码 * @param dexBytes */ private static void fixCheckSumHeader(byte[] dexBytes) { Adler32 adler = new Adler32(); adler.update(dexBytes, 12, dexBytes.length - 12);//从12到文件末尾计算校验码long value = adler.getValue(); int va = (int) value; byte[] newcs = intToByte(va); //高位在前,低位在前掉个个 byte[] recs = new byte[4]; for (int i = 0; i < 4; i++) { recs[i] = newcs[newcs.length - 1 - i]; System.out.println(Integer.toHexString(newcs[i])); } System.arraycopy(recs, 0, dexBytes, 8, 4);//效验码赋值(8-11) System.out.println(Long.toHexString(value)); System.out.println(); } /** * int 转byte[] * @param number * @return */ public static byte[] intToByte(int number) { byte[] b = new byte[4]; for (int i = 3; i >= 0; i--) { b[i] = (byte) (number % 256); number >>= 8; } return b; } /** * 修改dex头sha1值 * @param dexBytes * @throws NoSuchAlgorithmException */ private static void fixSHA1Header(byte[] dexBytes) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(dexBytes, 32, dexBytes.length - 32);//从32为到结束计算sha--1 byte[] newdt = md.digest(); System.arraycopy(newdt, 0, dexBytes, 12, 20);//修改sha-1值(12-31) //输出sha-1值,可有可无 String hexstr = ""; for (int i = 0; i < newdt.length; i++) { hexstr += Integer.toString((newdt[i] & 0xff) + 0x100, 16) .substring(1); } System.out.println(hexstr); } /** * 修改dex头file_size值 * @param dexBytes */ private static void fixFileSizeHeader(byte[] dexBytes) { //新文件长度 byte[] newfs = intToByte(dexBytes.length); System.out.println(Integer.toHexString(dexBytes.length)); byte[] refs = new byte[4]; //高位在前,低位在前掉个个 for (int i = 0; i < 4; i++) { refs[i] = newfs[newfs.length - 1 - i]; System.out.println(Integer.toHexString(newfs[i])); } System.arraycopy(refs, 0, dexBytes, 32, 4);//修改(32-35) } /** * 以二进制读出文件内容 * @param file * @return * @throws IOException */ private static byte[] readFileBytes(File file) throws IOException { byte[] arrayOfByte = new byte[1024]; ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream(); FileInputStream fis = new FileInputStream(file); while (true) { int i = fis.read(arrayOfByte); if (i != -1) { localByteArrayOutputStream.write(arrayOfByte, 0, i); } else { return localByteArrayOutputStream.toByteArray(); } } } } 下面来分析一下: 红色部分其实就是最核心的工作: 1>、加密源程序Apk文件 [java] view plain copy byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));//以二进制形式读出apk,并进行加密处理//对源Apk进行加密操作 加密算法很简单: [java] view plain copy //直接返回数据,读者可以添加自己加密方法 private static byte[] encrpt(byte[] srcdata){ for(int i = 0;i srcdata[i] = (byte)(0xFF ^ srcdata[i]); } return srcdata; } 对每个字节进行异或一下即可。 (说明:这里是为了简单,所以就用了很简单的加密算法了,其实为了增加破解难度,我们应该使用更高效的加密算法,同事最好将加密操作放到native层去做) 2>、合并文件:将加密之后的Apk和原脱壳Dex进行合并 [java] view plain copy int payloadLen = payloadArray.length; int unShellDexLen = unShellDexArray.length; int totalLen = payloadLen + unShellDexLen +4;//多出4字节是存放长度的。 byte[] newdex = new byte[totalLen]; // 申请了新的长度 //添加解壳代码 System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);//先拷贝dex内容 //添加加密后的解壳数据 System.arraycopy(payloadArray, 0, newdex, unShellDexLen, payloadLen);//再在dex内容后面拷贝apk的内容 3>、在文件的末尾追加源程序Apk的长度 [java] view plain copy //添加解壳数据长度 System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4);//最后4为长度 4>、修改新Dex文件的文件头信息:file_size; sha1; check_sum [java] view plain copy //修改DEX file size文件头 fixFileSizeHeader(newdex); //修改DEX SHA1 文件头 fixSHA1Header(newdex); //修改DEX CheckSum文件头 fixCheckSumHeader(newdex); 具体修改可以参照之前说的文件头格式,修改指定位置的字节值即可。 这里我们还需要两个输入文件: 1>、源Apk文件:ForceApkObj.apk 2>、脱壳程序的Dex文件:ForceApkObj.dex 那么第一个文件我们都知道,就是上面的源程序编译之后的Apk文件,那么第二个文件我们怎么得到呢?这个就是我们要讲到的第三个项目:脱壳程序项目,他是一个Android项目,我们在编译之后,能够得到他的classes.dex文件,然后修改一下名称就可。 3、脱壳项目:ReforceApk 在讲解这个项目之前,我们先来了解一下这个脱壳项目的工作: 1>、通过反射置换android.app.ActivityThread 中的mClassLoader为加载解密出APK的DexClassLoader,该DexClassLoader一方面加载了源程序、另一方面以原mClassLoader为父节点,这就保证了即加载了源程序又没有放弃原先加载的资源与系统代码。 关于这部分内容,不了解的同学可以看一下ActivityThread.java的源码: 如何得到系统加载Apk的类加载器,然后我们怎么将加载进来的Apk运行起来等问题都在这篇文章中说到了。 2>、找到源程序的Application,通过反射建立并运行。 这里需要注意的是,我们现在是加载一个完整的Apk,让他运行起来,那么我们知道一个Apk 运行的时候都是有一个Application对象的,这个也是一个程序运行之后的全局类。所以我们必须找到解密之后的源Apk的Application类,运行的他的onCreate方法,这样源Apk才开始他的运行生命周期。这里我们如何得到源Apk的Application的类呢?这个我们后面会说道。使用meta标签进行设置。 下面来看一下代码: [java] view plain copy package com.example.reforceapk; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import https://www.360docs.net/doc/bf762368.html,ng.ref.WeakReference; import https://www.360docs.net/doc/bf762368.html,ng.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import android.app.Application; import android.app.Instrumentation; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import https://www.360docs.net/doc/bf762368.html,NotFoundException; import android.content.res.AssetManager; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.os.Bundle; import android.util.ArrayMap; import android.util.Log; import dalvik.system.DexClassLoader; public class ProxyApplication extends Application{ private static final String appkey = "APPLICATION_CLASS_NAME"; private String apkFileName; private String odexPath; private String libPath; //这是context 赋值 @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); try { //创建两个文件夹payload_odex,payload_lib 私有的,可写的文件目录 File odex = this.getDir("payload_odex", MODE_PRIV ATE); File libs = this.getDir("payload_lib", MODE_PRIVA TE); odexPath = odex.getAbsolutePath(); libPath = libs.getAbsolutePath(); apkFileName = odex.getAbsolutePath() + "/payload.apk"; File dexFile = new File(apkFileName); Log.i("demo", "apk size:"+dexFile.length()); if (!dexFile.exists()) { dexFile.createNewFile(); //在payload_odex文件夹内,创建payload.apk // 读取程序classes.dex文件 byte[] dexdata = this.readDexFileFromApk(); // 分离出解壳后的apk文件已用于动态加载 this.splitPayLoadFromDex(dexdata); } // 配置动态加载环境 Object currentActivityThread = RefInvoke.invokeStaticMethod( "android.app.ActivityThread", "currentActivityThread", new Class[] {}, new Object[] {});//获取主线程对象https://www.360docs.net/doc/bf762368.html,/myarrow/article/details/14223493 String packageName = this.getPackageName();//当前apk的包名 //下面两句不是太理解 ArrayMap mPackages = (ArrayMap) RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mPackages"); WeakReference wr = (WeakReference) mPackages.get(packageName); //创建被加壳apk的DexClassLoader对象加载apk内的类和本地代码(c/c++代码) DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath, libPath, (ClassLoader) RefInvoke.getFieldOjbect( "android.app.LoadedApk", wr.get(), "mClassLoader")); //base.getClassLoader(); 是不是就等同于(ClassLoader) RefInvoke.getFieldOjbect()? 有空验证下//? //把当前进程的DexClassLoader 设置成了被加壳apk的DexClassLoader ----有点c++中进程环境的意思~~ RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader", wr.get(), dLoader); Log.i("demo","classloader:"+dLoader); try{ Object actObj = dLoader.loadClass("com.example.forceapkobj.MainActivity"); Log.i("demo", "actObj:"+actObj); }catch(Exception e){ Log.i("demo", "activity:"+Log.getStackTraceString(e)); } } catch (Exception e) { Log.i("demo", "error:"+Log.getStackTraceString(e)); e.printStackTrace(); } } @Override public void onCreate() { { //loadResources(apkFileName); Log.i("demo", "onCreate"); // 如果源应用配置有Appliction对象,则替换为源应用Applicaiton,以便不影响源程序逻辑。 String appClassName = null; try { ApplicationInfo ai = this.getPackageManager() .getApplicationInfo(this.getPackageName(), PackageManager.GET_META_DATA); Bundle bundle = ai.metaData; if (bundle != null && bundle.containsKey("APPLICA TION_CLASS_NAME")) { appClassName = bundle.getString("APPLICATION_CLASS_NAME");//className 是配置在xml文件中的。 } else { Log.i("demo", "have no application class name"); return; } } catch (NameNotFoundException e) { Log.i("demo", "error:"+Log.getStackTraceString(e)); e.printStackTrace(); } //有值的话调用该Applicaiton Object currentActivityThread = RefInvoke.invokeStaticMethod( "android.app.ActivityThread", "currentActivityThread", new Class[] {}, new Object[] {}); Object mBoundApplication = RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mBoundApplication"); Object loadedApkInfo = RefInvoke.getFieldOjbect( "android.app.ActivityThread$AppBindData", mBoundApplication, "info"); //把当前进程的mApplication 设置成了null RefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication", loadedApkInfo, null); Object oldApplication = RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mInitialApplication"); //https://www.360docs.net/doc/bf762368.html,/article/android-context.html ArrayList .getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mAllApplications"); mAllApplications.remove(oldApplication);//删除oldApplication ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke .getFieldOjbect("android.app.LoadedApk", loadedApkInfo, "mApplicationInfo"); ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke .getFieldOjbect("android.app.ActivityThread$AppBindData", mBoundApplication, "appInfo"); appinfo_In_LoadedApk.className = appClassName; appinfo_In_AppBindData.className = appClassName; Application app = (Application) RefInvoke.invokeMethod( "android.app.LoadedApk", "makeApplication", loadedApkInfo, new Class[] { boolean.class, Instrumentation.class }, new Object[] { false, null });//执行makeApplication(false,null)RefInvoke.setFieldOjbect("android.app.ActivityThread", "mInitialApplication", currentActivityThread, app); ArrayMap mProviderMap = (ArrayMap) RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mProviderMap"); Iterator it = mProviderMap.values().iterator(); while (it.hasNext()) { Object providerClientRecord = it.next(); Object localProvider = RefInvoke.getFieldOjbect( "android.app.ActivityThread$ProviderClientRecord", providerClientRecord, "mLocalProvider"); RefInvoke.setFieldOjbect("android.content.ContentProvider", "mContext", localProvider, app); } Log.i("demo", "app:"+app); app.onCreate(); } } /** * 释放被加壳的apk文件,so文件 * @param data * @throws IOException */ private void splitPayLoadFromDex(byte[] apkdata) throws IOException { int ablen = apkdata.length; //取被加壳apk的长度这里的长度取值,对应加壳时长度的赋值都可以做些简化byte[] dexlen = new byte[4]; System.arraycopy(apkdata, ablen - 4, dexlen, 0, 4); ByteArrayInputStream bais = new ByteArrayInputStream(dexlen); DataInputStream in = new DataInputStream(bais); int readInt = in.readInt(); System.out.println(Integer.toHexString(readInt)); byte[] newdex = new byte[readInt]; //把被加壳apk内容拷贝到newdex中 System.arraycopy(apkdata, ablen - 4 - readInt, newdex, 0, readInt); //这里应该加上对于apk的解密操作,若加壳是加密处理的话 //? //对源程序Apk进行解密 newdex = decrypt(newdex); //写入apk文件 File file = new File(apkFileName); try { FileOutputStream localFileOutputStream = new FileOutputStream(file); localFileOutputStream.write(newdex); localFileOutputStream.close(); } catch (IOException localIOException) { throw new RuntimeException(localIOException); } //分析被加壳的apk文件 ZipInputStream localZipInputStream = new ZipInputStream( new BufferedInputStream(new FileInputStream(file))); while (true) { ZipEntry localZipEntry = localZipInputStream.getNextEntry();//不了解这个是否也遍历子目录,看样子应该是遍历的 if (localZipEntry == null) { localZipInputStream.close(); break; } //取出被加壳apk用到的so文件,放到libPath中(data/data/包名/payload_lib) String name = localZipEntry.getName(); if (name.startsWith("lib/") && name.endsWith(".so")) { File storeFile = new File(libPath + "/" + name.substring(https://www.360docs.net/doc/bf762368.html,stIndexOf('/'))); storeFile.createNewFile(); FileOutputStream fos = new FileOutputStream(storeFile); byte[] arrayOfByte = new byte[1024]; while (true) { int i = localZipInputStream.read(arrayOfByte); if (i == -1) break; fos.write(arrayOfByte, 0, i); } fos.flush(); fos.close(); } localZipInputStream.closeEntry(); } localZipInputStream.close(); } /** * 从apk包里面获取dex文件内容(byte) * @return * @throws IOException */ private byte[] readDexFileFromApk() throws IOException { ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream(); ZipInputStream localZipInputStream = new ZipInputStream( new BufferedInputStream(new FileInputStream( this.getApplicationInfo().sourceDir))); while (true) { ZipEntry localZipEntry = localZipInputStream.getNextEntry(); if (localZipEntry == null) { localZipInputStream.close(); break; } if (localZipEntry.getName().equals("classes.dex")) { byte[] arrayOfByte = new byte[1024]; while (true) { int i = localZipInputStream.read(arrayOfByte); if (i == -1) break; dexByteArrayOutputStream.write(arrayOfByte, 0, i); } } localZipInputStream.closeEntry(); } localZipInputStream.close(); return dexByteArrayOutputStream.toByteArray(); } // //直接返回数据,读者可以添加自己解密方法 private byte[] decrypt(byte[] srcdata) { for(int i=0;i srcdata[i] = (byte)(0xFF ^ srcdata[i]); } return srcdata; } //以下是加载资源 protected AssetManager mAssetManager;//资源管理器 protected Resources mResources;//资源 protected Theme mTheme;//主题 protected void loadResources(String dexPath) { try { AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, dexPath); mAssetManager = assetManager; } catch (Exception e) { Log.i("inject", "loadResource error:"+Log.getStackTraceString(e)); e.printStackTrace(); } Resources superRes = super.getResources(); superRes.getDisplayMetrics(); superRes.getConfiguration(); mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(),superRes.getConfiguration()); mTheme = mResources.newTheme(); mTheme.setTo(super.getTheme()); } @Override public AssetManager getAssets() { return mAssetManager == null ? super.getAssets() : mAssetManager; } @Override public Resources getResources() { return mResources == null ? super.getResources() : mResources; } @Override public Theme getTheme() { return mTheme == null ? super.getTheme() : mTheme; } } 首先我们来看一下具体步骤的代码实现: 1>、得到脱壳Apk中的dex文件,然后从这个文件中得到源程序Apk.进行解密,然后加载 [java] view plain copy //这是context 赋值 @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); try { //创建两个文件夹payload_odex,payload_lib 私有的,可写的文件目录 File odex = this.getDir("payload_odex", MODE_PRIV ATE); File libs = this.getDir("payload_lib", MODE_PRIVA TE); odexPath = odex.getAbsolutePath(); libPath = libs.getAbsolutePath(); apkFileName = odex.getAbsolutePath() + "/payload.apk"; File dexFile = new File(apkFileName); Log.i("demo", "apk size:"+dexFile.length()); if (!dexFile.exists()) { dexFile.createNewFile(); //在payload_odex文件夹内,创建payload.apk // 读取程序classes.dex文件 byte[] dexdata = this.readDexFileFromApk(); // 分离出解壳后的apk文件已用于动态加载 this.splitPayLoadFromDex(dexdata); } // 配置动态加载环境 Object currentActivityThread = RefInvoke.invokeStaticMethod( "android.app.ActivityThread", "currentActivityThread", new Class[] {}, new Object[] {});//获取主线程对象https://www.360docs.net/doc/bf762368.html,/myarrow/article/details/14223493 String packageName = this.getPackageName();//当前apk的包名 //下面两句不是太理解 ArrayMap mPackages = (ArrayMap) RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mPackages"); WeakReference wr = (WeakReference) mPackages.get(packageName); //创建被加壳apk的DexClassLoader对象加载apk内的类和本地代码(c/c++代码)DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath, libPath, (ClassLoader) RefInvoke.getFieldOjbect( "android.app.LoadedApk", wr.get(), "mClassLoader")); //base.getClassLoader(); 是不是就等同于(ClassLoader) RefInvoke.getFieldOjbect()? 有空验证下//? //把当前进程的DexClassLoader 设置成了被加壳apk的DexClassLoader ----有点c++中进程环境的意思~~ RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader", wr.get(), dLoader); Log.i("demo","classloader:"+dLoader); try{ Object actObj = dLoader.loadClass("com.example.forceapkobj.MainActivity"); Log.i("demo", "actObj:"+actObj); }catch(https://www.360docs.net/doc/bf762368.html,eption e){ Log.i("demo", "activity:"+Log.getStackTraceString(e)); } } catch (Exception e) { Log.i("demo", "error:"+Log.getStackTraceString(e)); e.printStackTrace(); } } 这里需要注意的一个问题,就是我们需要找到一个时机,就是在脱壳程序还没有运行起来的时候,来加载源程序的Apk,执行他的onCreate方法,那么这个时机不能太晚,不然的话,就是运行脱壳程序,而不是源程序了。查看源码我们知道。Application中有一个方法:attachBaseContext这个方法,他在Application的onCreate方法执行前就会执行了,那么我们的工作就需要在这里进行 1)、从脱壳程序Apk中找到源程序Apk,并且进行解密操作 [java] view plain copy //创建两个文件夹payload_odex,payload_lib 私有的,可写的文件目录 File odex = this.getDir("payload_odex", MODE_PRIV A TE); File libs = this.getDir("payload_lib", MODE_PRIV ATE); odexPath = odex.getAbsolutePath(); libPath = libs.getAbsolutePath(); apkFileName = odex.getAbsolutePath() + "/payload.apk"; File dexFile = new File(apkFileName); Log.i("demo", "apk size:"+dexFile.length()); if (!dexFile.exists()) { dexFile.createNewFile(); //在payload_odex文件夹内,创建payload.apk // 读取程序classes.dex文件 byte[] dexdata = this.readDexFileFromApk(); // 分离出解壳后的apk文件已用于动态加载 this.splitPayLoadFromDex(dexdata); } 这个脱壳解密操作一定要和我们之前的加壳以及加密操作对应,不然就会出现Dex加载错误问题 A) 从Apk中获取到Dex文件 [java] view plain copy * 从apk包里面获取dex文件内容(byte) * @return * @throws IOException */ private byte[] readDexFileFromApk() throws IOException { ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream(); ZipInputStream localZipInputStream = new ZipInputStream( new BufferedInputStream(new FileInputStream( this.getApplicationInfo().sourceDir))); while (true) { ZipEntry localZipEntry = localZipInputStream.getNextEntry(); if (localZipEntry == null) { localZipInputStream.close(); break; } if (localZipEntry.getName().equals("classes.dex")) { byte[] arrayOfByte = new byte[1024]; while (true) { int i = localZipInputStream.read(arrayOfByte); if (i == -1) break; dexByteArrayOutputStream.write(arrayOfByte, 0, i); } } localZipInputStream.closeEntry(); } localZipInputStream.close(); return dexByteArrayOutputStream.toByteArray(); } 其实就是解压Apk文件,直接得到dex文件即可 B) 从脱壳Dex中得到源Apk文件 [java] view plain copy /** * 释放被加壳的apk文件,so文件 * @param data * @throws IOException */ private void splitPayLoadFromDex(byte[] apkdata) throws IOException { int ablen = apkdata.length; //取被加壳apk的长度这里的长度取值,对应加壳时长度的赋值都可以做些简化byte[] dexlen = new byte[4]; System.arraycopy(apkdata, ablen - 4, dexlen, 0, 4); ByteArrayInputStream bais = new ByteArrayInputStream(dexlen); DataInputStream in = new DataInputStream(bais); int readInt = in.readInt(); System.out.println(Integer.toHexString(readInt)); byte[] newdex = new byte[readInt]; //把被加壳apk内容拷贝到newdex中 System.arraycopy(apkdata, ablen - 4 - readInt, newdex, 0, readInt); //这里应该加上对于apk的解密操作,若加壳是加密处理的话 //? //对源程序Apk进行解密 newdex = decrypt(newdex); //写入apk文件 File file = new File(apkFileName); try { FileOutputStream localFileOutputStream = new FileOutputStream(file); localFileOutputStream.write(newdex); localFileOutputStream.close(); } catch (IOException localIOException) { throw new RuntimeException(localIOException); } //分析被加壳的apk文件 ZipInputStream localZipInputStream = new ZipInputStream( new BufferedInputStream(new FileInputStream(file))); while (true) { ZipEntry localZipEntry = localZipInputStream.getNextEntry();//不了解这个是否也遍历子目录,看样子应该是遍历的 if (localZipEntry == null) { localZipInputStream.close(); break; } //取出被加壳apk用到的so文件,放到libPath中(data/data/包名/payload_lib) String name = localZipEntry.getName(); if (name.startsWith("lib/") && name.endsWith(".so")) { File storeFile = new File(libPath + "/" + name.substring(https://www.360docs.net/doc/bf762368.html,stIndexOf('/'))); storeFile.createNewFile(); FileOutputStream fos = new FileOutputStream(storeFile); byte[] arrayOfByte = new byte[1024]; while (true) { int i = localZipInputStream.read(arrayOfByte); if (i == -1) ----------------<小A分>---------------- 一、概论 壳出于程序作者想对程序资源压缩、注册保护的目的,把壳分为压缩壳和加密壳(强壳)两种 "UPX" "ASPCAK" "TELOCK" "PELITE" "NSPACK(北斗)" ... "ARMADILLO" "ASPROTECT" "ACPROTECT" "EPE(王)" "SVKP" ... 顾名思义,压缩壳只是为了减小程序体积对资源进行压缩,加密壳是程序输入表等等进行加密保护。 当然加密壳的保护能力要强得多! -----------<小A分割线>------------- 二、工具的认识 OllyDBG ring3 shell层级别的动态编译工具、 PEid、 ImportREC、 LordPE、 softIce ring0级别调试工具 -------------<小A分割>------------------- 三、常见手动脱壳方法 预备知识 1.PUSHAD (入栈/压栈)代表程序的入口点, 2.POPAD (弹栈/出栈)代表程序的出口点,与PUSHAD想对应,一般找到这个OEP就在附近 3.OEP:程序的入口点,软件加壳就是隐藏了OEP(或者用了假的OEP/FOEP),只要我们找到程序真正的OEP,就可以立刻脱壳。 ------------<小A分割线>-------------------- 方法一:单步跟踪法 1.用OD载入,点“不分析代码!” 2.单步向下跟踪F8,实现向下的跳。也就是说向上的跳不让其实现!(通过F4) 3.遇到程序往回跳的(包括循环),我们在下一句代码处按F4(或者右健单击代码,选择断点——>运行到所选) 4.绿色线条表示跳转没实现,不用理会,红色线条表示跳转已经实现! 5.如果刚载入程序,在附近就有一个CALL的,我们就F7跟进去,不然程序很容易跑飞,这样很快就能到程序的OEP 6.在跟踪的时候,如果运行到某个CALL程序就运行的,就在这个CALL中F7进入 7.一般有很大的跳转(大跨段),比如 jmp XXXXXX 或者 JE XXXXXX 或者有RETN 的一般很快就会到程序的OEP。 近CALL F7 远CALL F8 Btw:在有些壳无法向下跟踪的时候,我们可以在附近找到没有实现的大跳转,右键-->“跟随”,然后F2下断,Shift+F9运行停在“跟随”的位置,再取消断点, 第一种:匿名内部类作为事件监听器类 大部分时候,事件处理器都没有什么利用价值(可利用代码通常都被抽象成了业务逻辑方法),因此大部分事件监听器只是临时使用一次,所以使用匿名内部类形式的事件监听器更合适,实际上,这种形式是目前是最广泛的事件监听器形式。上面的程序代码就是匿名内部类来创建事件监听器的!!! 对于使用匿名内部类作为监听器的形式来说,唯一的缺点就是匿名内部类的语法有点不易掌握,如果读者java基础扎实,匿名内部类的语法掌握较好,通常建议使用匿名内部类作为监听器。 第二种:内部类作为监听器 将事件监听器类定义成当前类的内部类。1、使用内部类可以在当前类中复用监听器类,因为监听器类是外部类的内部类,2、所以可以自由访问外部类的所有界面组件。这也是内部类的两个优势。上面代码就是内部类的形式!! 第三种:Activity本身作为事件监听器 这种形式使用activity本身作为监听器类,可以直接在activity类中定义事件处理器方法,这种形式非常简洁。但这种做法有两个缺点:(1)这种形式可能造成程序结构混乱。Activity 的主要职责应该是完成界面初始化;但此时还需包含事件处理器方法,从而引起混乱。(2)如果activity界面类需要实现监听器接口,让人感觉比较怪异。 上面的程序让Activity类实现了OnClickListener事件监听接口,从而可以在该Activity类中直接定义事件处理器方法:onClick(view v),当为某个组件添加该事件监听器对象时,直接使用this作为事件监听器对象即可。 第四种:外部类作为监听器 ButtonTest类 当用户单击button按钮时,程序将会触发MyButtonListener监听器 外部MyButtonListener类 《Android基础应用》 AndroidUI基本控件与事件处理 ?本章任务 ?使用Android开发本息计算器程序 ?使用Android开发华氏-摄氏温度转换器 ?本章目标 ?熟悉掌握本章基本控件的使用 ?熟练掌握Android常用事件 1.Android基本控件 Android应用开发的一项内容就是用户界面的开发,Android提供了大量功能丰富的UI组件,大部分放在android.widget包及其子包android.view包及其子包 在Android当中View类是最基本的一个UI类,基本上所有的高级UI组件都是继承View类而实现的。如Button(按钮),list(列表),EditText(编辑框),RadioButton(多选按钮),Checkbox(选择框)等都是View类 在Android中,我们可以在Xml文件中使用UI组件也可以在java文件中创建UI组件官方建议采用xml方式,这样的话能够实现界面和代码分离 1.1TextView和EditText TextView是一种用于显示字符串的控件 EditText则是用来输入和编辑字符串的控件,EditText是一个具有编辑功能的TextView TextView和EditText基本属性 ●android:id设置ID,通过编码可以找到这个组件 ●android:layout_width设置在屏幕上的宽度 ●android:layout_height设置在屏幕上的高度 fill_parent强制性地使构件扩展,以填充布局单元内尽可能多的空间 wrap_content强制性地使视图扩展以显示全部内容 ●android:text设置显示的文本信息 ●android:textColor设置文本颜色 ●android:textSize设置文本尺寸 第二节脱壳 一切从“壳”开始 吴朝相1999.2.23 (搜新网https://www.360docs.net/doc/bf762368.html,) 我写这篇东西的主要目的是让初到本站的新手们能对“壳”有个大概的认识,知道我每天说了些什么。限于本人的知识,如果有ERROR 之处,还请多原谅。如果你觉得还可以,也欢迎转贴,但请保留文章的完整性和作者的资料。当然如果你想把它发表,硬塞些稿费给俺花花,我也不会拒绝的。;) 作为一个以“壳”为主的站台,如果连访者连什么是“壳”都不清楚的话,那我也太失败了。很早以前就想写编完全关于“壳”的文章,但苦于时间和文字水平的关系,都没提笔。本着对站台负责的态度,现在经过一天的努力,“打”出这编尝试由壳的历史一直谈到最新发展的本章来。 首先我想大家应该先明白“壳”的概念。在自然界中,我想大家对壳这东西应该都不会陌生了,植物用它来保护种子,动物用它来保护身体等等。同样,在一些计算机软件里也有一段专门负责保护软件不被非法修改或反编译的程序。它们一般都是先于程序运行,拿到控制权,然后完成它们保护软件的任务。就像动植物的壳一般都是在身体外面一样理所当然(但后来也出现了所谓的“壳中带籽”的壳)。由于这段程序和自然界的壳在功能上有很多相同的地方,基于命名的规则,大家就把这样的程序称为“壳”了。就像计算机病毒和自然界的病毒一样,其实都是命名上的方法罢了。 最早提出“壳”这个概念的,据我所知,应该是当年推出脱壳软件RCOPY 3 的作者熊焰先生。在几年前的DOS 时代,“壳”一般都是指磁盘加密软件的段加密程序,可能是那时侯的加密软件还刚起步不久吧,所以大多数的加密软件(加壳软件)所生成的“成品”在“壳”和需要加密的程序之间总有一条比较明显的“分界线”。有经验的人可以在跟踪软件的运行以后找出这条分界线来,至于这样有什么用这个问题,就不用我多说了。但毕竟在当时,甚至现在这样的人也不是很多,所以当RCOPY3 这个可以很容易就找出“分界线”,并可以方便的去掉“壳”的软件推出以后,立即就受到了很多人的注意。老实说,这个我当年在《电脑》杂志看到广告,在广州电脑城看到标着999元的软件,在当时来说,的确是有很多全新的构思,单内存生成EXE 可执行文件这项,就应该是世界首创了。但它的思路在程序的表现上我认为还有很多可以改进的地方(虽然后来出现了可以加强其功力的RO97),这个想法也在后来和作者的面谈中得到了证实。在这以后,同类型的软件想雨后春笋一般冒出来,记得住名字的就有:UNKEY、MSCOPY、UNALL .... 等等,但很多的软件都把磁盘解密当成了主攻方向,忽略了其它方面,当然这也为以后的“密界克星”“解密机器”等软件打下了基础,这另外的分支就不多祥谈了,相信机龄大一点的朋友都应该看过当时的广告了。 解密(脱壳)技术的进步促进、推动了当时的加密(加壳)技术的发展。LOCK95和BITLOK 等所谓的“壳中带籽”加密程序纷纷出笼,真是各出奇谋,把小小的软盘也折腾的够辛苦的了。正在国内的加壳软件和脱壳软件较量得正火红的时候,国外的“壳”类软件早已经发展到像LZEXE 之类的压缩壳了。这类软件说穿了其实就是一个标准的加壳软件,它把EXE 文件压缩了以后,再在文件上加上一层在软件被执行的时候自动把文件解压缩的“壳”来达到压缩EXE 文件的目的。接着,这类软件也越来越多,PKEXE、AINEXE、UCEXE 和后来被很多人认识的WWPACK 都属于这类软件,但奇怪的是,当时我看不到一个国产的同类软件。 过了一段时间,可能是国外淘汰了磁盘加密转向使用软件序列号的加密方法吧,保护EXE 文件不被动态跟踪和静态反编译就显得非常重要了。所以专门实现这样功能的加壳程序便诞生了。MESS 、 加壳与脱壳的应用与实现 一、加壳 (2) 1.什么是壳 (2) 2.加壳原因 (2) 3.壳的加载过程 (3) 4.压缩引擎 (5) 5.常见的加壳工具 (6) a.常用压缩壳介绍 (6) b.加密保护壳介绍 (7) 二、脱壳 (10) 1.侦壳 (10) 2.脱壳 (13) a.查找程序的真正入口点(OEP) (13) b.抓取内存映像文件 (15) c.输入表重建 (15) 附:视频“加壳与脱壳(软件)”和“手动脱壳” (17) 加壳与脱壳 一、加壳 1.什么是壳 在一些计算机软件里也有一段专门负责保护软件不被非法修改或反编译的程序。它们一般都是先于程序运行,拿到控制权,然后完成它们保护软件的任务。由于这段程序和自然界的壳在功能上有很多相同的地方,基于命名的规则,就把这样的程序称为“壳”了。 图1.1 2.加壳原因 就把这样的程序称为“壳”了。 作者编好软件后,编译成exe可执行文件。 1)有一些版权信息需要保护起来,不想让别人随便改动,如作者 的姓名,即为了保护软件不被破解,通常都是采用加壳来进行保护。 2)需要把程序搞的小一点,从而方便使用。于是,需要用到一些软件,它们能将exe可执行文件压缩。 3)在黑客界给木马等软件加壳脱壳以躲避杀毒软件。实现上述功能,这些软件称为加壳软件。 3.壳的加载过程 1)获取壳自己所需要使用的API地址 如果用PE编辑工具查看加壳后的文件,会发现未加壳的文件和加壳后的文件的输入表不一样,加壳后的输入表一般所引入的DLL和API函数很少,甚至只有Kernel32.dll以及GetProcAddress这个API 函数。 壳实际上还需要其他的API函数来完成它的工作,为了隐藏这些API,它一般只在壳的代码中用显式链接方式动态加载这些API函数2)解密原程序的各个区块(Section)的数据 壳出于保护原程序代码和数据的目的,一般都会加密原程序文件的各个区块。在程序执行时外壳将会对这些区块数据解密,以让程序能正常运行。壳一般按区块加密的,那么在解密时也按区块解密,并且把解密的区块数据按照区块的定义放在合适的内存位置。 如果加壳时用到了压缩技术,那么在解密之前还有一道工序,当然是解压缩。这也是一些壳的特色之一,比如说原来的程序文件未加壳时1~2M大小,加壳后反而只有几百K。 VB加壳脱壳程序源码 1、窗体代码 Private Sub Check1_Click() Text2.SetFocus End Sub Private Sub Image2_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) Image10.Visible = False End Sub Private Sub Image3_Click() If Text1.Text = "" Then MsgBox "Please Select A File First!", vbInformation Else List1.Visible = True List2.Visible = False Frame3.Visible = False List1.Text = " UPX 1.24 " Text2.SetFocus End If End Sub Private Sub Command2_Click() Dim path As String, back_path As String, file_t As String 'Dim's strings Text2.SetFocus CommonDialog1.ShowOpen Text1.Text = CommonDialog1.FileName path = Text1.Text back_path = "Backupfile.exe" If Check1.Value = 1 Then i = FreeFile Open path For Binary As #i file_t = Space(LOF(i)) Get #i, , file_t Close #i Open back_path For Binary As #i Put #i, , file_t Close #i MsgBox " A Backup of the file has been created in the same location as the original file", vbInformation End If End Sub Private Sub Image3_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) Image8.Visible = True End Sub Private Sub Image3_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) Image8.Visible = False Android OnTouchListener触屏事件接口 OnTouchListener接口是用来处理手机屏幕事件的监听接口,当为View的范围内触摸按下、抬起或滑动等动作时都会触发该事件。该接口中的监听方法签名如下。 Java代码: public boolean onT ouch(View v, MotionEvent event) 参数v:参数v同样为事件源对象。 参数event:参数event为事件封装类的对象,其中封装了触发事件的详细信息,同样包括事件的类型、触发时间等信息。 节中介绍了一个在屏幕中拖动矩形移动的案例,本节将继续采用该案例的思路,通过监听接口的方式实现在屏幕上拖动按钮移动的案例。开发步骤如下。 创建一个名为Sample的Android项目。 准备字符串资源,打开strings.xml文件,用下列代码替换原有代码。 Java代码: Java代码: 第二课用相应工具软件为软件自动脱壳(非手动脱壳) 欲破解一个软件,我们首先应根据前面的内容侦测它的壳,然后我们要把它的壳脱去,还原软件的本来面目。如果软件是一个PLMM,我们不喜欢穿衣服的MM,我们不喜欢艺术照的MM,我们迫不及待地想把MM脱光,想把MM骗上床。带壳的软件以后很难分析,带壳的穿衣的MM很难调教,壳是一个拦路虎,我们却不知武松醉在何处。这就如同我们要吃糖炒栗子,必须先剥掉栗子壳一样。这一课就教给你如何用自动剥壳机去掉花生壳、栗子壳之类的东东。 若侦测出它根本没加壳,就可省掉这一步了(现在没加壳的软件已经很少很少了,除非软件作者缺乏最基本的加密解密常识)。 脱壳成功的标志是脱壳后的文件能正常运行,功能没有任何损耗。一般来说,脱壳后的文件长度大于原文件长度;即使同一个文件,当采用不同脱壳软件进行脱壳的时候,由于脱壳软件机理不同,脱出来的文件大小也不尽相同。但只要能够运行起来,这都是正常的,就如同人的体重,每次上秤,份量都有所不同。但只要这个人是健康的,就无所谓,合乎情理。 一、脱壳软件的两大类别(两个门派――少林、武当) 脱壳软件主要分两大类:专用脱壳软件(武当派)和通用脱壳软件(少林派,源自“全民皆武,天下英雄出少年”)。每个专用脱壳软件只能脱掉特定的一种或两种加壳软件所加的壳,也就是说它是专门针对某种加壳软件的某个版本而制作的。通用脱壳软件则具有通用性,可以脱掉许多种不同类型的壳。根据“以一当一”的原则,专用类此门派为“武当”派。 大家可能会有这样的疑问?既然有通用脱壳软件,为什么还要专用脱壳软件呢?所谓“术业有专攻”,通用的脱壳程序往往不能精确地适用于某些软件,而专用的脱壳程序适用面虽窄,对付特定的壳却极为有效。因此,少林派和武当派缺一不可,相辅相成。 均掌握了武术之精髓,能置“壳”以死地,打得壳满地找牙,第一时间以迅雷不及掩耳之势极速脱掉壳MM的衣衫。 二、专用脱壳软件的四大类别 根据壳的流行程度,常用的脱壳软件主要有三类:脱Aspack类(叉A)、脱UPX类(叉U)、脱pecompact类(叉P,怎么又跟微软搞到一块儿了),分别针对前面提到的3个加壳软件。剩下的全都归到一个其他类中。下面详细介绍它们的使用方法: (一)脱ASPack壳软件(叉A)――重点 针对ASPack壳的软件主要有两个: AspackDie,AsprStripper。 1. AspackDie(A死) AspackDie (作者网站https://www.360docs.net/doc/bf762368.html,,需要代理才能访问)支持ASPack 2.11/2.11c/2.11d/2.12版所加壳的脱壳。此软件的优点是支持新版ASPack壳、图形界面、使用方便、操作简单。最新版本1.41。 一、概论 壳出于程序作者想对程序资源压缩、注册保护的目的,把壳分为压缩壳和加密壳两种 UPX ASPCAK TELOCK PELITE NSPACK ... ARMADILLO ASPROTECT ACPROTECT EPE SVKP ... 顾名思义,压缩壳只是为了减小程序体积对资源进行压缩,加密壳是程序输入表等等进行加密保护。当然加密壳的保护能力要强得多! 二、常见脱壳方法 预备知识 1.PUSHAD (压栈)代表程序的入口点, 2.POPAD (出栈)代表程序的出口点,与PUSHAD想对应,一般找到这个OEP就在附近 3.OEP:程序的入口点,软件加壳就是隐藏了OEP(或者用了假的OEP/FOEP),只要我们找到程序真正的OEP,就可以立刻脱壳。 方法一:单步跟踪法 1.用OD载入,点“不分析代码!” 2.单步向下跟踪F8,实现向下的跳。也就是说向上的跳不让其实现!(通过F4) 3.遇到程序往回跳的(包括循环),我们在下一句代码处按F4(或者右健单击代码,选择断点——>运行到所选) 4.绿色线条表示跳转没实现,不用理会,红色线条表示跳转已经实现! 5.如果刚载入程序,在附近就有一个CALL的,我们就F7跟进去,不然程序很容易跑飞,这样很快就能到程序的OEP 6.在跟踪的时候,如果运行到某个CALL程序就运行的,就在这个CALL中F7进入 7.一般有很大的跳转(大跨段),比如jmp XXXXXX 或者JE XXXXXX 或者有RETN的一般很快就会到程序的OEP。 在有些壳无法向下跟踪的时候,我们可以在附近找到没有实现的大跳转,右键-->“跟随”,然后F2下断,Shift+F9运行停在“跟随”的位置,再取消断点,继续F8单步跟踪。一般情况下可以轻松到达OEP! 方法二:ESP定律法 ESP定理脱壳(ESP在OD的寄存器中,我们只要在命令行下ESP的硬件访问断点,就会一下来到程序的OEP了!) 1.开始就点F8,注意观察OD右上角的寄存器中ESP有没突现(变成红色)。(这只是一般情况下,更确切的说我们选择的ESP值是关键句之后的第一个ESP值) 2.在命令行下:dd XXXXXXXX(指在当前代码中的ESP地址,或者是hr XXXXXXXX),按回车! 3.选中下断的地址,断点--->硬件访--->WORD断点。 4.按一下F9运行程序,直接来到了跳转处,按下F8,到达程序OEP。 方法三:内存镜像法 软件脱壳、破解精典实例教程 我要破解的软件:网络填表终结者破解需要的软件(点击下载): 侦壳language.exe 脱壳AspackDie.exe 反编译W32Dasm黄金中文版 16进制编辑器UltraEdit.rar 在破解之前先复习一下基础知识: 一.破解的等级 初级,修改程序,用ultraedit修改exe文件,称暴力破解,简称爆破 中级,追出软件的注册码 高级,写出注册机 二.用w32dasm破解的一般步骤: 1.看软件的说明书,软件注册与不注册在功能上有什么区别,如何注册 2.运行此软件,试着输入你的姓名和任意注册码去注册,有什麽错误提示信息,将错误提示信息记下来 3.侦测有无加壳(第一课).若加壳,脱壳(第二课) 4.pw32dasmgold反汇编 5.串式参考中找到错误提示信息或可能是正确的提示信息双击鼠标左键 6.pw32dasmgold主窗口中分析相应汇编,找出关键跳转和关键call 7.绿色光条停在关键跳转,在pw32dasmgold主窗口底部找到关键跳转的偏移地址(实际修改地址) 8.用ultraedit找到偏移地址(实际修改地址)修改机器码,保存 壳的概念:版权信息需要保护起来,不想让别人随便改动,exe可执行文件压缩,最常见的加壳软件ASPACK ,UPX,PEcompact 脱壳:拿到一个软件,侦测它的壳,然后我们要把它的壳脱去,还原它的本来面目.若它没有加壳,就省事不用脱壳了.脱壳软件unaspack,caspr,upx,unpecompact,procdump 实际修改地址(偏移地址)和行地址(虚拟地址)pw32dasmgold反汇编出来的代码由三列组成 第一列行地址(虚拟地址) 第二列机器码(最终修改时用ultraedit修改) 广播事件处理 一.Broadcast Receiver 比如打电话等等; 广播接收器,它和事件处理机制类似,只不过事件处理机制是程序组件级别,而广播事件处理机制是系统级别。 二.使用Broadcast Receiver 1.编写类继承BroadcaseReceiver,复写onReceiver()方法 2.在AndroidManifest.xml文件中注册BroadcaseReceiver 3.构建Intent对象 4.调用sendBroadcase()方法发送广播 三.BroadcaseReceiver生命周期 BroadcastReceiver对象仅在调用onReceive()方法时有效,当该方法执行完毕后,系统认为销毁该对象。 四.标准广播Action 五.注册Broadcast Receiver的方法 1.在AndroidManifest.xml文件中进行注册//有缺陷,不会因为Activity被销毁而销毁,一般不用 应用程序加壳与脱壳 计算机与信息学院 信息安全专业11级1班 朱航宇 20112878 (1)实验目的 通过对灰鸽子自带的加壳程序,实现对改程序的加壳操作,并对加壳前后作出相应比较。从而了解什么是加壳,什么是脱壳,以及加壳的原理、作用。 (2) 实验内容 使用灰鸽子黑防专版生成加壳和未加壳程序;使用PEID测试加壳程序;使用UPXUnpack汉化版(利用OD脱壳)对已加壳的程序进行脱壳操作。 (3) 实验步骤 1. 加壳 1. 打开灰鸽子黑防专版。 2. 配置服务程序,生成不加壳程序。 点击工具栏上的“配置服务程序”按钮,自动上线设置中,ip设置为127.0.0.1;选择“高级选项”,选择 不加壳,设置保存路径保存路径当前文件夹,保存文件名称为 Server.exe ,然后点击 生成服务器。此时在系统桌面上将生成该程序。这个程 序即为没有加壳的灰鸽子程序。 3.配置服务程序,生成加壳程序。 在主界面点击配置服务程序,打开服务器配置对话框设置好IP地址为127.0.0.1,然后点击“高级选项”选择:使用UPX加壳,保存路径当前文件夹,保存文件名设置为Server_jiake.exe ,并点击生成服务器按钮。生成的程序如图1所示。 图1.灰鸽子生成加壳和不加壳的server 4.打开PEID程序;将这两个文件分别拖到PEiD程序界面。分别查看检测结果 ,以下两个图分别是未加壳和加了壳的检测结果,如图2,图3,可以看出,未加壳程序所检测出来的是文件开发工具,而加了壳的文件检测出来的是加壳信息。 图2.未加壳的检测 图3.加壳的检测 2. 脱壳 1、打开UPXUnpack汉化版程序,主界面如下;直接拖动 Server_jiake.exe文件到程序内,显示如图4所示,生成脱壳文件Server_tuoke。 图4.加壳server脱壳 2. 再次打开PEID程序,检测Server_tuoke.exe文件的加壳信息,如图5 所示。 可以看到它的信息和未加壳server是相同的。 Android进阶——Android事件分发机制之dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent 前言 Android事件分发机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦 Android事件分发机制的简介 Android事件分发机制的发生在View与View之间或者ViewGroup与View之间具有镶嵌的视图上,而且视图上必须为点击可用。当一个点击事件产生后,它的传递过程遵循如下顺序:Activity->Window->View,即事件先传递给Activity,再到Window,再到顶级View,才开始我们的事件分发 Android事件分发机制的相关概念 Android事件分发机制主要由三个重要的方法共同完成的 dispatchTouchEvent:用于进行点击事件的分发 onInterceptTouchEvent:用于进行点击事件的拦截 onTouchEvent:用于处理点击事件 这里需要注意的是View中是没有onInterceptTouchEvent()方法的 Android事件分发机制的分发例子 这里以两个ViewGroup嵌套View来演示,下面是演示图 一、MyView 继承View并覆写其三个构造方法,覆写dispatchTouchEvent和onTouchEvent,前面已经说 了View是没有onInterceptTouchEvent方法的 public class MyView extends View { public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent event) { System.out.println("MyView dispatchTouchEvent"); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { System.out.println("MyView onTouchEvent"); return super.onTouchEvent(event); } } 二、MyViewGroup01和MyViewGroup02 MyViewGroup01和MyViewGroup02是一样的代码,这里以01为例,继承ViewGroup并覆写其三个构造方法,覆写dispatchTouchEvent和onTouchEvent和onInterceptTouchEvent方法 public class MyViewGroup01 extends LinearLayout { public MyViewGroup01(Context context) { super(context); } public MyViewGroup01(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public MyViewGroup01(Context context, AttributeSet attrs) { 第27卷第9期 计算机应用与软件 Vol 127No .92010年9月 Computer App licati ons and Soft w are Sep.2010 PE 文件中脱壳技术的研究 李 露 刘秋菊 徐汀荣 (苏州大学计算机科学与技术学院 江苏苏州215006) 收稿日期:2009-01-07。李露,硕士生,主研领域:网络安全,信息技术。 摘 要 对PE (Portable Executable )文件进行加壳是保护软件的有效手段,但恶意程序也会通过加壳来保护自己。作为一名病毒 分析师或软件安全分析员,只有先将其脱壳,才能进行彻底的分析。以W indows 记事本程序为实例,首先分析了PE 文件结构及其加壳原理,其次阐述了脱壳的一般步骤,然后从压缩壳和加密壳的角度,重点探讨了脱壳技术的原理和方法。最后对伪装壳和多重壳及程序自校验进行了探讨和分析。 关键词 PE 加壳 脱壳 伪装壳 多重壳 自校验 O N UNPACK I NG TECHNOLO GY FO R PE F I L ES L i Lu L iu Q iuju Xu Tingr ong (School of Co m puter Science and Technology,Soocho w U niversity,Suzhou 215006,J iangsu,China ) Abstract Packing Portable Executable (PE )files is an effective mean t o p r otect s oft w are,but mal w are can als o use packing t o p r otect the m selves .A s a virus analyst or a s oft w are security researcher,you must unpack the mal w are first,then can you analysis the m in detail .Taking the notepad p r ogra m in M icr os oftW indows as an in this paper we first analyzed the PE file structure and the p rinci p le of packing,and then expounded the general step s of unpacking .After that,in ter m s of the comp ressi on shell and encryp ti on shell,we f ocused on the p rinci p les and methods of unpacking technol ogy .Finally,we discussed and analyzed the ca mouflage shell,multi 2shell and self 2chec 2king . Keywords PE Packing Unpacking Ca mouflage shell Multi 2shell Self 2checking 0 引 言 PE 文件格式是W I N 32环境自带的跨平台可执行文件格 式,常见的EXE 、DLL 、OCX 、SYS 、COM 等文件均是PE 格式。使用该格式,在非I ntel 芯片的CP U 上,W indows 一样能识别和使用。 对PE 文件加壳,能较好地保护原程序。但病毒和木马也会利用加壳技术来保护自己,因为加壳后程序执行结果不变,但代码发生了变化,从而使杀毒软件无法查杀。作为一名病毒分析师或者软件安全研究员,如果不懂得脱壳技术,将很难对这些恶意程序进行分析。据瑞星公司截获的病毒样本统计,90%以上的病毒文件都经过加壳处理,可见掌握脱壳技术十分重要。现有文章大都进行加壳技术的探讨[1-3],本文则着重研究脱壳技术。 1 壳的介绍 壳是一段附加在原程序上的代码,它先于真正的程序运行并拿到控制权,在完成程序保护任务后(检测程序是否被修改,是否被跟踪等),再将控制权转交给真正的程序,其运行过程与病毒有些相似。在形式上又与W I N RAR 类的压缩软件类似,运行前都需要将原程序解压。但壳对程序的解压是在内存中进行,对用户来说完全透明,用户感觉不到壳的存在。 壳分为压缩壳和加密壳。压缩壳只是为了减少程序体积而 对资源进行压缩,便于传输,具有一定的保护作用。常见的压缩壳有UPX 、ASPCAK 、TE LOCK 、PE L I TE 、NSP ACK 等。加密壳是使用各种手段对程序资源进行保护,防止其被反汇编或跟踪,文件加壳后是否变小不是其主要目标。常见的加密壳有AR MA 2D I L LO 、ASPROTECT 、ACPROTECT 、EPE 、S VKP 等。目前一些壳已兼具有两种功能,即能压缩资源,又能加密资源。 2 PE 结构框架 PE 文件使用一个平面地址空间,所有代码和数据都被合并 成一个很大的结构。文件内容由属性相同的区块组成,各区块按页边界对齐,大小没有限制。每个区块都有不同的名字,用来表示区块的功能。图1是W indows98下记事本Notepad .exe 程序的PE 结构图。从图中可以看出PE 文件由几个连续的区块组成。先后由DOS 头部、PE 头部、区段表以及各个区段组成。其中.text 是代码段,.data 是已初始化的数据段,.idata 是输入表段,.rsrc 是资源段,.rel oc 是基址重定位表段 。 图1 W indows98记事本PE 结构图 Android广播事件处理闹钟实例 对应AlarmManage有一个AlarmManagerServie服务程序,该服务程序才是正真提供闹铃服务的,它主要维护应用程序注册下来的各类闹铃并适时的设置即将触发的闹铃给闹铃设备(在系 统中,linux实现的设备名为”/dev/alarm”),并且一直监听闹铃设备,一旦有闹铃触发或 者是闹铃事件发生,AlarmManagerServie服务程序就会遍历闹铃列表找到相应的注册闹铃并 发出广播。该服务程序在系统启动时被系统服务程序system_service启动并初始化闹铃设备(/dev/alarm)。当然,在JAVA层的AlarmManagerService与Linux Alarm驱动程序接口之间 还有一层封装,那就是JNI。 AlarmManager将应用与服务分割开来后,使得应用程序开发者不用关心具体的服务,而 是直接通过AlarmManager来使用这种服务。这也许就是客户/服务模式的好处吧。AlarmManager与 AlarmManagerServie之间是通过Binder来通信的,他们之间是多对一的关系。 在android系统中,AlarmManage提供了3个接口5种类型的闹铃服务。 3个接口: 1. // 取消已经注册的与参数匹配的闹铃 2. void cancel(PendingIntent operation) 1. 2. //注册一个新的闹铃 3. void set(int type, long triggerAtTime, PendingIntent operation) 4. //注册一个重复类型的闹铃 5. void setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation) 6. //设置时区 7. void setTimeZone(String timeZone) 5个闹铃类型 public static final int ELAPSED_REALTIME 1. //当系统进入睡眠状态时,这种类型的闹铃不会唤醒系统。直到系统下次被唤醒才传 递它,该闹铃所用的时间是相对时间,是从系统启动后开始计时的,包括睡眠 2. 时间,可以通过调用SystemClock.elapsedRealtime()获得。系统值是 3 Testing和Instrumentation Android提供了一系列强大的测试工具,它针对Android的环境,扩展了业内标准的JUnit测试框架。尽管你可以使用JUnit 测试Android工程,但Android工具允许你为应用程序的各个方面进行更为复杂的测试,包括单元层面及框架层面。Android测试环境的主要特征有: ●可以访问Android系统对象。 ●Instrumentation框架可以控制和测试应用程序。 ●Android系统常用对象的模拟版本。 ●运行单个test或test suite的工具,带或不带Instrumentation。 ●支持以Eclipse的ADT插件和命令行方式管理Test和Test工程。 这篇文章是对Android测试环境和测试方法的简要介绍,并假设你已经拥有一定的Android应用程序编程及JUnit测试的经验。概要 Android测试环境的核心是一个Instrumentation框架,在这个框架下,你的测试应用程序可以精确控制应用程序。使用Instrumentation,你可以在主程序启动之前,创建模拟的系统对象,如Context;控制应用程序的多个生命周期;发送UI事件给应用程序;在执行期间检查程序状态。Instrumentation框架通过将主程序和测试程序运行在同一个进程来实现这些功能。 通过在测试工程的manifest文件中添加(完整版)常见几种脱壳方法
安卓按钮单击事件
AndroidUI基本控件与事件处理
怎样使用脱壳软件
加壳与脱壳应用及实现
VB加壳脱壳程序源码
Android OnTouchListener触屏事件接口
第2课用相应工具软件为软件自动脱壳(非手动脱壳)
壳的介绍以及是常用脱壳方法
软件脱壳破解精典实例教程
android简单的广播事件处理
朱航宇-20112878-应用程序加壳与脱壳
Android进阶——Android事件分发机制之dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
PE文件中脱壳技术的研究
Android广播事件处理闹钟实例
主动发送事件 android