我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android 逆向学习详解及实例

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android 逆向学习详解及实例

         断断续续的总算的把android开发和逆向的这两本书看完了,虽然没有java,和android开发的基础,但总体感觉起来还是比较能接收的,毕竟都是触类旁通的。当然要深入的话还需要对这门语言的细节特性和奇技淫巧进行挖掘。

  这里推荐2本书,个人觉得对android开发入门和android逆向入门比较好的教材:

  《google android 开发入门与实战》

  《android 软件安全与逆向分析》

  1. 我对android逆向的认识

  因为之前有一些windows逆向的基础,在看android逆向的时候感觉很多东西都是能共通的。但因为android程序本身的特性,还是有很多不同的地方。

  1.1 反编译

  android程序使用java语言编写,从java到android虚拟机(Dalvik)的dex代码(可以看成是android虚拟机的机器码)需要一个中间语言的转换过程。类似.NET的IL中间虚拟指令。而我们知道,.NET的IL中间代码之所以能很容易的"反编译"回C#源代码,是因为除了IL中间语言,还包含了大量的META元数据,这些元数据使我们可以很容易的一一对应的反编译回C#的源代码。java的中间语言.class文件也是类似的道理,我们可以使用工具直接从dex机器码反编译回java源代码。

  1.2 逆向分析手段

  windows的逆向分析中,我们可以使用OD或者C32ASM来分析汇编指令(当然OD还可以动态调试),或者使用IDA + F5(hex Ray反编译插件)来静态的分析源代码(C/C++)

  在android逆向分析过程中:

  1) 我们可以使用ApkTool(本质上是BakSmali反汇编引擎)对apk文件进行反汇编,得到各个类、方法、资源、布局文件...的smali代码,我们可以直接通过阅读smali代码来分析程序的代码流,进行关键点的修改或者代码注入。

  2) 我们可以从apk中提取.dex文件,使用dex2jar工具对dex进行反汇编,得到jar包(java虚拟指令),然后使用jd-gui等工具再次反编译,得到java源代码,从源码级的高度来审计代码,更快的找到关键点函数或者判断,然后再回到smali层面,对代码进行修改。这种方法更倾向于辅助性的,最终的步骤我们都要回到smali层面来修改代码。

  3) 使用IDA Pro直接分析APK包中的.dex文件,找到关键点代码的位置,记下文件偏移量,然后直接对.dex文件进行修改。修改完之后把.dex文件重新导入apk中。这个时候要注意修改dex文件头中DexHeader中的checksum字段。将这个值修复后,重新导入apk中,并删除apk中的META-INF文件夹,重新签名即可完成破解。

  1.3 android与C的结合

  在学习android逆向的时候感觉遇到的最难的问题就是分析原生代码,即JNI代码。开发者使用android NDK编写C/C++代码供android的java代码调用(通过java的代码转接层来完成接口的转换)。

  使用android NDK编写的C/C++代码最终会生成基于ARM的ARM ELF可执行文件,我们想要分析软件的功能就必须掌握另一项技能,ARM汇编,ARM汇编个人感觉虽然和x86汇编类似,不过由于IDA Pro对ARM汇编没有反编译功能以及貌似没有工具能动态调试ARM代码(我网上没找到),导致我们只能直接硬看ARM代码,加上往往伴随着复杂的密码学算法等等,导致对Native Code的逆向相对来说比较困难,对基本功的要求比较高。

  1.4 关于分析android程序

  1) 了解程序的AndroidManifest.xml。在程序中使用的所有activity(交互组件)都需要在AndroidManifest.xml文件中手动声明。包括程序启动时默认启动的主activity,通过研究这个AndroidManifest.xml文件,我们可以知道该程序使用了多少的activity,主activity是谁,使用了哪些权限,使用了哪些服务,做到心中有数。

  2) 重点关注Application类

  这本来和1) AndroidManifest.xml是一起的,但是分出来说是因为这个思路和windows下的逆向思路有相通之处。

  在windows exe的数据目录表中如果存在TLS项,那程序在加载后会首先执行这个TLS中的代码,执行完之后才进行main主程序入口。

  在android 中Application类比程序中其他的类启动的都要早。

  3) 定位关键代码

  3.1) 信息反馈法(关键字查找法)

  通过运行程序,查找程序UI中出现的提示消息或标题等关键字,到String.xmlzhong中查找指定字符串的di,然后到程序中查找指定的id即可。

  3.2) 特征函数法

  这种做法的原理和信息反馈法类似,因为不管你提示什么消息,就必然会调用相应的API函数来显示这个字符串,例如Toast.MakeText().show()

  例如在程序中搜索Toast就有可能很快地定位到调用代码

  3.3) 代码注入法

  代码注入法属于动态调试的方法,我们可以手动修改smali反汇编代码,加入Log输入,配合LogCat来查看程序执行到特定点时的状态数据。

  3.4) 栈跟踪法

  栈跟踪法属于动态调试方法,从原理上和我们用OD调试时查看call stack的思想类似。我们可以在smali代码中注入输出运行时的栈跟踪信息,然后查看栈上的函数调用序列来理解方法的执行流程(因为每个函数的执行都会在栈上留下记录)

  3.5) Method Profiling

  Method Profiling,方法剖析(这是书上的叫法,我更愿意叫BenchMark测试法),它属于一种动态调试方法,它主要用于热点分析和性能优化。在DDMS中有提供这个功能,它除了可记录每个函数所占用的CPU时间外,还能够跟踪所有的函数调用关系。

  1.5 关于android的代码混淆和加壳

  java语言编写的代码本身就很容易被反编译,google为此在android 2.3的SDK中正式加入了ProGuard代码混淆工具,只要正确的配置好project.properties与proguard.cfg两个文件即可使用ProGuard混淆软件。

  java语言由于语言自身的特殊性,没有外壳保护这个概念,只能通过混淆方式对其进行保护。对android NDK编写的Native Code倒是可以进行加壳,但目前貌似只能进行ups的压缩壳保护

  2. CrackMe_1 分析学习

  2.1 运行一下程序,收集一些基本信息

  只有一个输入框,那说明这个验证码的输入来自别的地方,因为我们知道,不管你的加密算法是啥,总是要有一个函数输入源的,我们在UI界面上输入的相当于是结果,而输入源应该来自于别的地方,计算完之后和我们在UI上输入的结果进行对比,大致是这个思路。

  2.2 分析

  使用apktool反编译apk文件。查看AndroidManifest.xml文件。了解到主activity为:Main。

  接着我们从apk中提取.dex文件。用dex2jar->jd-gui来查看java源代码。

  看到里面很多的a,b,c方法,基本上可以判定是配ProGuard混淆了,不过问题也不大,虽然显示的是无意义的函数名但是不影响我们分析代码流程。

  2.2.1 类b的分析

  从OnCreate()的代码来看,我们首先从类b开始分析:

  类 b 提供了一个公共的构造函数 public b(Context paramContext),  一个私有的成员函数private String b(),  以及一个公有成员函数 public final void a()。

  b(): 通过TelephonyManager获取设备相关的一些信息,然后通过PackageManager获取到自身的签名。然后把这些字符串拼接起来返回给调用者。


TelephonyManager localTelephonyManager = (TelephonyManager)this.a.getSystemService("phone"); 
  String str1 = localTelephonyManager.getDeviceId(); 
  String str2 = localTelephonyManager.getLine1Number(); 
  String str3 = localTelephonyManager.getDeviceSoftwareVersion(); 
  String str4 = localTelephonyManager.getSimSerialNumber(); 
  String str5 = localTelephonyManager.getSubscriberId(); 
  Object localObject = ""; 
  PackageManager localPackageManager = this.a.getPackageManager(); 
  try 
  { 
   String str6 = localPackageManager.getPackageInfo("com.lohan.crackme1", 64).signatures[0].toCharsString(); 
   localObject = str6; 
   return str1 + str2 + str3 + str4 + str5 + (String)localObject; 
  } 
a(): 
 SharedPreferences localSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this.a); 
  SharedPreferences.Editor localEditor; 
  if (!localSharedPreferences.contains("machine_id")) 
   localEditor = localSharedPreferences.edit(); 
  try 
  { 
   localEditor.putString("machine_id", b()); 
   localEditor.commit(); 
   return; 
  } 

  a()调用方法b()获取字符串,然后通过SharedPreferences.Editor将这个字符串值存储到键machine_id,可以理解为机器码。也就是说,这个加密函数的输入是本机的机器码。

  经过上面的分析,类b对外提供方法a,功能就是生成"机器码"并存储到系统中,对应的键为machine_id。

  2.2.2 类c的分析

  类c提供的方法较多,我们逐个分析。

  1) 构造函数

Java代码


public c(Context paramContext) 
{ 
  a = paramContext; 
  b = "f0d412b5530e1f9841aab434d989cc77"; 
  c = "4ec407446b872351e613111339daae9"; 
} 

  把参数环境上下文Context本地化,并声明了两个字符串。

  2) public static boolean b()

Java代码


MessageDigest localMessageDigest = MessageDigest.getInstance("MD5"); 
localMessageDigest.update(paramString.getBytes(), 0, paramString.length()); 
return new BigInteger(1, localMessageDigest.digest()).toString(16); 

  通过MessageDigest计算paramString 的MD5值。

  3) public static boolean b()

Java代码


PackageManager localPackageManager = a.getPackageManager(); 
  try 
  { 
   String str = b(new String(localPackageManager.getPackageInfo("com.lohan.crackme1", 64).signatures[0].toChars())); 
   if (!str.equals(b)) 
   { 
    boolean bool = str.equals(c); 
    if (!bool); 
   } 
   else 
   { 
    return false; 
   } 
  } 

  通过 getPackageManager 获取自身的签名,如果签名与构造函数中的两个字符串b(f0d412b5530e1f9841aab434d989cc77)或者c(4ec407446b872351e613111339daae9)任意一个相等,那么返回false,否则返回true。

  4) public static int a(String paramString)

Java代码


try 
{ 
 if (b()) 
  return 0; 
 SharedPreferences localSharedPreferences = PreferenceManager.getDefaultSharedPreferences(a); 
 if (b(localSharedPreferences.getString("machine_id", "")).equals(paramString)) 
 { 
  if (b()) 
   return 0; 
  SharedPreferences.Editor localEditor = localSharedPreferences.edit(); 
  localEditor.putString("serial", paramString); 
  localEditor.commit(); 
  return 1; 
 } 
}

  可以看出这段代码的功能为计算机器码的 MD5,如果与传入的参数paramString一致,那么通过SharedPreferences存入到serial(机器码的MD5值paramString)字段中。 当然还有调用b方法进行一些判断,自身的签名不能是已知的两个。

  5) public static boolean a()

Java代码


SharedPreferences localSharedPreferences = PreferenceManager.getDefaultSharedPreferences(a); 
if (!localSharedPreferences.contains("serial")) 
 return false; 
String str = localSharedPreferences.getString("serial", ""); 
if (str.equals("")) 
 return false; 
return a(str) >= 0;

  这个其实就是上面的 int a(String paramString)的包装函数,通过SharedPreferences获取serial字段(机器码的MD5值),并传给这个方法,返回相应的返回值(判断结果)。

  2.2.3 类a分析

  可以看到,类a是一个CountDownTimer:

  Schedule a countdown until a time in the future, with regular notifications on intervals along the way. Example of showing a 30 second countdown in a text field:(android Developer)

  从onFinish函数我们看出这个类的功能是倒计时6秒,然后调用c.a(),也就是判断我们输入的serial是否等于"机器码"的MD5值。如果不能通过,就设置TextView内容提示注册。

  2.2.4 类Main分析

  1) 在onCreate(),先初始化b和c的类。然后调用b.a()生成并存储"机器码",然后调用c.a(),也就是判断是否已经存储了serial,并判断是否能通过算法校验。如果不能通过,则什么都不做,这就是启动时检测注册状态的做法,即如果你之前已经注册了,那在之后的登录后就会自动识别出来,但是我们如果是第一次启动且没有注册,那这里就什么也不做。

  如果能通过,则调用自身的方法a()。而自身的方法a()又调用了c.b()方法,即检查我们输入的serial和机器码的MD5值是否相同,如果相同则什么也不做,如果不同就把下面的按钮和TextView等UI控件给隐藏了。并启动倒计时类a.start()。即二次验证。

  ps:

  这里要注意的是,由于程序使用了ProGuard来混淆代码,所以用jd-gui翻译出来的代码全都是从a,b,c开始计数,而且经常是变量、类、方法的命名混合了起来。我们在看java代码的时候遇到难懂的地方要结合smali代码一起看,这样才能获取比较准确的对程序代码流的把握。

  2) public void onClick(View paramView)

Java代码


if (c.a(((EditText)findViewById(2131034114)).getText().toString()) == 0) 
{ 
 Toast.makeText(this, 2130968577, 0).show(); 
 return; 
} 
Toast.makeText(this, 2130968578, 0).show(); 

  判断我们通过UI输入的serial是否和"机器码"的MD5值相同,如果不相同则弹出提示Invalid serial!(可以通过ID值反查出对应的字符串),如果相同则弹出Thanks for purchasing!

  通过以上分析,我们来综合一下思路:

  程序启动时会做一些初始化的工作,然后生成本地对应的机器码并保存在SharedPreferences中。

  检查当前的SharedPreferences中是否已经保存了serial键值对,并检查正确性,即检查是否上一次已经注册了。如果没有这个键值对,说明还没注册,如果存在这个键值对且正确性也符合,代码接下来会继续检查APK自身的签名是否为代码中定义的那两个,如果相等则什么都不做(即依然不通过检查),如果不等则代码继续执行倒计时6秒的类a, 6秒后再次检查一次serial键值对。

  对于那个按钮点击事件,onClick(),它获取用户通过UI输入的serial,并检测是否和"机器码"的MD5值相等,如果相等则存进SharedPreferences中的键值对中。

  以上基本就是这个程序的代码思路了。我们可以看到,作者这里使用了双重保护的思路,即不仅要你输入的serial相同,而且对你的APK的签名也有限制。

  3. 破解思路

  3.1 单纯的破解,用代码注入的方法得到注册码。

  经过分析,我们知道应该在b.smali的155行:

  move-result-object v2 这里代码注入,因为这个b()的作用就是获取当前"机器码"(注意,这里获取的是没有MD5之前的"机器码",因为程序中的MD5都是临时算出来的)

  我们在这里加入:

  const-string v3, "SN"

  invoke-static {v3, v2}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I

  重新回编译smalli代码。

  在命令行中执行 adb logcat -s SN:v  ,然后再启动程序

  会在命令行中看到一大串字符串,这些字符串就是我们要的机器码

  将这些字符串计算MD5值之后,就可以完成破解了。

  3.2 读取程序对应的文件

  我们知道,所谓的SharedPreferences本质上是保存在当前程序空间下的/data/data/<package name>/shared_prefs/<package name>_preferences.xml文件中的。

  我们可以通过adb连接上去,直接读取这个文件的内容。

  可以看到,和我们通过代码注入的方式得到的机器码是相同的。

  3.3 编写注册机

  这种方法是最好的,编写注册机要求我们对目标程序的代码有全盘的认识,然后模拟原本的算法或者逆向原本的算法写出注册机

  我们用Eclipse重新生成一个新的工程 com.lohan.crackme。注意,工程的报名必须和目标程序的包名一致,这样我们的注册机运行后得到的APK签名才会是一样的。

  核心算法如下:

Java代码


@Override 
protected void onCreate(Bundle savedInstanceState)  
{ 
  super.onCreate(savedInstanceState); 
  setContentView(R.layout.activity_main); 
  setTitle("crackMe1_keyGen"); 
  final Context context = getApplicationContext(); 
  //获取UI控件 
  txt_machineCode = (TextView) findViewById(R.id.machineCode); 
  txt_apkSig = (TextView) findViewById(R.id.apkSig); 
  txt_serial = (TextView) findViewById(R.id.serial); 
  btn_Go = (Button) findViewById(R.id.ok); 
  //设置监听事件 
  btn_Go.setOnClickListener(new OnClickListener(){ 
    public void onClick(View v) 
    { 
      //计算机器码 
      TelephonyManager localTelephonyManager = (TelephonyManager) context.getSystemService("phone"); 
      String str1 = localTelephonyManager.getDeviceId(); 
      String str2 = localTelephonyManager.getLine1Number(); 
      String str3 = localTelephonyManager.getDeviceSoftwareVersion(); 
      String str4 = localTelephonyManager.getSimSerialNumber(); 
      String str5 = localTelephonyManager.getSubscriberId(); 
      Object localObject = ""; 
      PackageManager localPackageManager = context.getPackageManager(); 
      try 
      { 
       String str6 = localPackageManager.getPackageInfo("com.lohan.crackme1", 64).signatures[0].toCharsString(); 
       localObject = str6; 
       String str_result = str1 + str2 + str3 + str4 + str5 + (String)localObject; 
       //得出机器码 
       txt_machineCode.setText(str_result); 
       //计算当前APK的签名 
       txt_apkSig.setText(str6); 
       //计算注册码 
       MessageDigest localMessageDigest = null;  
        try { 
          localMessageDigest = MessageDigest.getInstance("MD5"); 
        } catch (NoSuchAlgorithmException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
        }  
       localMessageDigest.update(str_result.getBytes(), 0, str_result.length()); 
       String str_serial = new BigInteger(1, localMessageDigest.digest()).toString(16); 
       txt_serial.setText(str_serial); 
      } 
      catch (PackageManager.NameNotFoundException localNameNotFoundException) 
      { 
       while (true) 
        localNameNotFoundException.printStackTrace(); 
      }  
    } 
  }); 

  破解结果

  APK:

  http://pan.baidu.com/s/1qsygp

  4. 总结

  至此,这个android的CrackeMe_1就算破解完成了。这段时间的android学习也算暂时告一段落,移动无线安全是未来的新方向,在不远的将来,基于android平台的各种应用和软件不仅仅是手机甚至是各种的互联终端都将进入人们的视野,无线安全的研究应该也会慢慢成为热点。

  我也希望下次再研究android安全的时候能有更深入的认识和体会。

        有兴趣的同学可以看下本文,谢谢大家对本站的支持!

您可能感兴趣的文章:Android APK优化工具Zipalign详解Android样式的开发:layer-list实例详解Android自定义圆形倒计时进度条AndroidSDK Support自带夜间、日间模式切换详解Android 动态高斯模糊效果教程利用Android中BitmapShader制作自带边框的圆形头像Android中使用ViewStub实现布局优化Android优质索尼滚动相册Android ViewPager实现Banner循环播放Android Zipalign工具优化Android APK应用


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android 逆向学习详解及实例

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android 逆向学习详解及实例

断断续续的总算的把android开发和逆向的这两本书看完了,虽然没有java,和android开发的基础,但总体感觉起来还是比较能接收的,毕竟都是触类旁通的。当然要深入的话还需要对这门语言的细节特性和奇技淫巧进行挖掘。这里
2022-06-06

Android多线程学习实例详解

本文实例分析了Android多线程。分享给大家供大家参考,具体如下: 在Android下面也有多线程的概念,在C/C++中,子线程可以是一个函数,一般都是一个带有循环的函数,来处理某些数据,优先线程只是一个复 杂的运算过程,所以可能不需要w
2022-06-06

SpringBoot整合MyBatis逆向工程及 MyBatis通用Mapper实例详解

一、添加所需依赖,当前完整的pom文件如下:2023-05-31

Android编程之SurfaceView学习示例详解

本文实例讲述了Android编程之SurfaceView学习示例。分享给大家供大家参考,具体如下: SurfaceView是View的子类,使用的方式与任何View所派生的类都是完全相同的,可以像其他View那样应用动画,并把它们放到布局中
2022-06-06

Android WebView的详解及实例

Android WebView的详解 Android WebView在android平台上是一个特殊的View, 他能用来显示网页,这个类可以被用来在你的app中仅仅显示一张在线的网页,还可以用来开发浏览器。 在Androi
2023-05-30

Android ViewFlipper的详解及实例

Android ViewFlipper的详解前言:View Flipper,是ViewAnimator的子类,而ViewAnimator又是继承自FrameLayout,而FrameLayout就是平时基本上只显示一个子视图的布局,由于Fr
2023-05-30

Android游戏开发学习之引擎用法实例详解

本文实例讲述了Android游戏开发学习之引擎用法。分享给大家供大家参考。具体如下: 汽车引擎是汽车的心脏,其决定了汽车的性能和稳定性,是人们在购车时相当关注的。而游戏中的物理引擎就如汽车的引擎一样,占据了非常重要的位置。一款好的物理引擎可
2022-06-06

Android ToolBar控件详解及实例

ToolBar控件详解 在Activity中添加ToolBar1.添加库dependencies {...compile "com.android.support:appcompat-v7:18.0.+" }2.Activity要继承App
2022-06-06

Android RecyclerView详解及简单实例

Android RecyclerView 小白今天第一次接触RecyclerView,前辈大神告诉我这是一个很神奇的控件,一看就是一整天。RecyclerView中有规定好的方法去显示列表,图片甚至视频。还带有删除新建某一列表的方法。相对
2022-06-06

Android ViewPagerIndicator详解及实例代码

Android ViewPagerIndicator详解及实例代码关于自定义View的属性零碎知识自定义View和自定义属性的知识不再此提及,这里着重说的是属性在自定义View中的获取方式,自定义的属性如下:2023-05-31

Android ToggleButton 详解及实例代码

Android ToggleButton 详解 在Android的开发过程中,对于ToggleButton的使用频率也是相当的高的,下面我就来说一下,这个组件的两种使用方式。 第一种是简单的使用,利用Toast的方式弹出提示语句 需要注意的
2022-06-06

Android CoordinatorLayout详解及实例代码

Android CoordinatorLayout详解 一、CoordinatorLayout有什么作用 CoordinatorLayout作为“super-powered FrameLayout”基本实现两个功能: 1、作为顶层布局
2022-06-06

Android WebView 详解及简单实例

WebView基本使用WebView是View的一个子类,可以让你在activity中显示网页可以在布局文件中写入WebView:比如下面这个写了一个填满整个屏幕的WebView:2022-06-06

Android IntentService详解及使用实例

Android IntentService详解 一、IntentService简介 IntentService是Service的子类,比普通的Service增加了额外的功能。先看Service本身存在两个问题: Service不会专门启动
2022-06-06

Android Tab 控件详解及实例

Android Tab 控件详解及实例 在桌面应用中Tab控件使用得非常普遍,那么我们经常在Android中也见到以Tab进行布局的客户端。那么Android中的Tab是如何使用的呢? 1.Activitypackage com.wicre
2022-06-06

Android canvas drawBitmap方法详解及实例

Android canvas drawBitmap方法详解及实例 之前自己在自定义view,用到canvas.drawBitmap(Bitmap, SrcRect, DesRect, Paint)的时候,对其中的第2和3个参数的含义含糊不
2022-06-06

Android 开发中Volley详解及实例

Android 开发中Volley详解及实例 最近在做项目的时候,各种get和post。简直要疯了,我这种啥都不了解的,不知道咋办了,然后百度看了下,可以用volley进行网络请求与获取,下面就介绍下volley的用法。 volley有三种
2022-06-06

Android ListView position详解及实例代码

我们在使用ListView的时候,一般都会为ListView添加一个响应事件android.widget.AdapterView.OnItemClickListener。对OnItemClickListener的position和id参数,
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录