当前位置: 首页 > news >正文

初步了解安卓逆向

初步了解安卓逆向

目的

了解so层和java层,然后了解安卓逆向题目

so文件

  • 它相当于Windows下的.dll动态链接库(一种共享库文件,包含了程序所需的代码和数据,它的优势是使得程序的内存占用更小,同时也方便了程序的更新和维护)
  • native原生层
  • 作为C/C++ 编译后的二进制文件(那么就可以拖到ida里面进行分析)
  • 在这个so文件下,(在ctf里面)是经常会夹杂一些安全性手段的(比如加密,加壳反调试)

JAVA层

  • 可读性高
  • 进行交互的层
  • 而它是裸露的,会加杂安全手段(但是一般ctf不会为难,而到真实的小程序或者软件就会有混淆等)

先从例题入手

复习一下

[HZNUCTF 2023 preliminary]easyAPK

还是拖入jadx,按照常规操作进行分析

锁定了在这两个函数,点击,在example.easyapk.llooggiinn(login其实就是登录,那么这个就是入口)得到了具体的加密逻辑,

packagecom.example.easyapk;importandroid.content.Intent;importandroid.os.Bundle;importandroid.view.View;importandroid.widget.Button;importandroid.widget.EditText;importandroid.widget.Toast;importandroidx.appcompat.app.AppCompatActivity;importjava.security.InvalidAlgorithmParameterException;importjava.security.InvalidKeyException;importjava.security.NoSuchAlgorithmException;importjava.util.Base64;importjavax.crypto.BadPaddingException;importjavax.crypto.Cipher;importjavax.crypto.IllegalBlockSizeException;importjavax.crypto.NoSuchPaddingException;importjavax.crypto.spec.IvParameterSpec;importjavax.crypto.spec.SecretKeySpec;/* loaded from: classes3.dex */publicclassllooggiinnextendsAppCompatActivity{staticStringstr1="admin";staticStringstr2;ButtonloginBtn;EditTextpasswdEt;ButtonsignBtn;EditTextuserEt;@Override// androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.ActivityprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);this.userEt=(EditText)findViewById(R.id.user);this.passwdEt=(EditText)findViewById(R.id.passwd);this.loginBtn=(Button)findViewById(R.id.loginBtn);this.signBtn=(Button)findViewById(R.id.signBtn);try{Stringkey=getString(R.string.keyishere);byte[]ciphertext=Base64.getDecoder().decode("Lz49p2OjPZzUMXakynHQuw==");byte[]plaintext=decrypt(ciphertext,key.getBytes(),"iviviviviviviviv".getBytes());str2=newString(plaintext);}catch(InvalidAlgorithmParameterException|InvalidKeyException|NoSuchAlgorithmException|BadPaddingException|IllegalBlockSizeException|NoSuchPaddingExceptione){e.printStackTrace();}this.loginBtn.setOnClickListener(newView.OnClickListener(){// from class: com.example.easyapk.llooggiinn.1@Override// android.view.View.OnClickListenerpublicvoidonClick(Viewview){Stringusername=llooggiinn.this.userEt.getText().toString();Stringpasswd=llooggiinn.this.passwdEt.getText().toString();if(passwd.equals("")|username.equals("")){Toast.makeText(llooggiinn.this,"想空手套白狼?没门!",0).show();return;}if(!username.equals(llooggiinn.str1)||!passwd.equals(llooggiinn.str2)){Toast.makeText(llooggiinn.this,"用户名或密码不正确",0).show();return;}Intentintent=newIntent(llooggiinn.this,(Class<?>)ffuucc.class);llooggiinn.this.startActivity(intent);Toast.makeText(llooggiinn.this,"恭喜你,离成功又进一步",0).show();}});this.signBtn.setOnClickListener(newView.OnClickListener(){// from class: com.example.easyapk.llooggiinn.2@Override// android.view.View.OnClickListenerpublicvoidonClick(Viewview){Toast.makeText(llooggiinn.this,"不支持注册喵~\n快去找登录名和密码",0).show();}});}@Override// androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, android.app.ActivityprotectedvoidonStart(){super.onStart();}publicstaticbyte[]decrypt(byte[]data,byte[]key,byte[]iv)throwsBadPaddingException,NoSuchPaddingException,IllegalBlockSizeException,NoSuchAlgorithmException,InvalidKeyException,InvalidAlgorithmParameterException{Ciphercipher=Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(2,newSecretKeySpec(key,"AES"),newIvParameterSpec(iv));byte[]result=cipher.doFinal(data);returnresult;}}

明文先经过base64加密,给到key(要点击keyishere),前期我犯了错误,忘记了key应该是16位,所以进行搜索得到了正解key

,然后key经过AES加密,得到新的key,而密文又经过了一轮AES/CBC/PKCS5Pading(这里需要回顾一下AES加密)

总结一下思路

线索:

1.AES-CBC模式(key=kkkeyyy4044044刚好16位)

2.base64加密明文(Lz49p2OjPZzUMXakynHQuw==)

3.偏移值iv=iviviviviviviviv(也是刚好16位)

fromCrypto.CipherimportAESimportbase64 key=b"kkkeyyy404404404"iv=b"iviviviviviviviv"ct_b64="Lz49p2OjPZzUMXakynHQuw=="ct=base64.b64decode(ct_b64)cipher=AES.new(key,AES.MODE_CBC,iv)pt=cipher.decrypt(ct)# 先打印原始结果,方便判断print("解密原始数据:",pt)# 尝试不同的去除填充方式try:fromCrypto.Util.Paddingimportunpad pt_pkcs7=unpad(pt,AES.block_size)print("PKCS7去除填充成功:",pt_pkcs7.decode("utf-8"))except:print("PKCS7去除填充失败,尝试ZeroPadding...")pt_zero=pt.rstrip(b'\x00')print("ZeroPadding去除后:",pt_zero.decode("utf-8",errors="ignore"))

根据这个脚本我们得到了

卡了,因为我得到的并不知道该怎么用回到题目本身,打开它,这里用到雷电模拟器打开它

用户名这里得到admin,加上上面得到的密钥

得到这一串

得到flag

ezandroid.pro

又重新刷上了moectf的题目,害,还是不牢固啊啊啊啊啊!

打开可以看到是一个flag验证的程序

然后拖入jadx去找主入口

packagecom.example.ezandroidpro;importandroid.os.Bundle;importandroid.view.View;importandroid.widget.Button;importandroid.widget.EditText;importandroid.widget.TextView;importandroidx.appcompat.app.AppCompatActivity;/* loaded from: classes.dex */publicclassMainActivityextendsAppCompatActivity{privateEditTextinputEditText;privateTextViewresultTextView;publicnativebooleancheck(Stringstr);static{System.loadLibrary("ezandroidpro");}@Override// androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.ActivityprotectedvoidonCreate(Bundlebundle){super.onCreate(bundle);setContentView(R.layout.activity_main);this.inputEditText=(EditText)findViewById(R.id.inputEditText);this.resultTextView=(TextView)findViewById(R.id.resultTextView);((Button)findViewById(R.id.checkButton)).setOnClickListener(newView.OnClickListener(){// from class: com.example.ezandroidpro.MainActivity.1@Override// android.view.View.OnClickListenerpublicvoidonClick(Viewview){StringstrTrim=MainActivity.this.inputEditText.getText().toString().trim();if(strTrim.length()!=32){MainActivity.this.resultTextView.setText("flag长度不对,请重新输入");MainActivity.this.resultTextView.setTextColor(MainActivity.this.getResources().getColor(android.R.color.holo_red_dark));}elseif(MainActivity.this.check(strTrim)){MainActivity.this.resultTextView.setText("Congratulations!");MainActivity.this.resultTextView.setTextColor(MainActivity.this.getResources().getColor(android.R.color.holo_green_dark));}else{MainActivity.this.resultTextView.setText("Incorrect");MainActivity.this.resultTextView.setTextColor(MainActivity.this.getResources().getColor(android.R.color.holo_red_dark));}}});}}

只有对程序运行结果的评判以及flag的长度为32没有看到什么加密逻辑或者明文什么的,这时候就要去研究so文件

bool __fastcallJava_com_example_ezandroidpro_MainActivity_check(inta1,inta2,inta3){_BOOL4 v5;// r5constchar*s;// r0constchar*src;// r4size_t n0xB;// r0size_t n;// r5char*dest;// r9unsignedintv11;// r10char*_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649C;// r4intn96;// r2intv14;// r0unsigned __int8*v15;// r2intv16;// r1char*_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649;// r3void*s1_1;// r6unsigned __int8 v20;// [sp+4h] [bp-44h] BYREF_BYTE v21[7];// [sp+5h] [bp-43h] BYREFvoid*s1;// [sp+Ch] [bp-3Ch]_DWORD v23[2];// [sp+10h] [bp-38h] BYREFvoid*moectf2025______;// [sp+18h] [bp-30h]_DWORD v25[2];// [sp+1Ch] [bp-2Ch] BYREFvoid*dest_1;// [sp+24h] [bp-24h]v5=0;s=(constchar*)(*(int(__fastcall**)(int,int,_DWORD))(*(_DWORD*)a1+676))(a1,a3,0);if(!s)returnv5;src=s;n0xB=strlen(s);if(n0xB>=0xFFFFFFF0)std::__basic_string_common<true>::__throw_length_error(v25);n=n0xB;if(n0xB>=0xB){v11=(n0xB+16)&0xFFFFFFF0;dest=(char*)operatornew(v11);v25[1]=n;dest_1=dest;v25[0]=v11+1;gotoLABEL_7;}LOBYTE(v25[0])=2*n0xB;dest=(char*)v25+1;if(n0xB)LABEL_7:j_memcpy(dest,src,n);dest[n]=0;(*(void(__fastcall**)(int,int,constchar*))(*(_DWORD*)a1+680))(a1,a3,src);moectf2025______=(void*)operatornew(0x20u);strcpy((char*)moectf2025______,"moectf2025!!!!!!");v23[1]=16;v23[0]=33;_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649C=(char*)operatornew(0x70u);strcpy(_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649C,"4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649C0B971BF2EFBCB160E531A646DF7A6AC0B");sm4Encrypt(&v20,v25,v23);n96=*(_DWORD*)&v21[3];v14=v20&1;if(!v14)n96=v20>>1;if(n96==96){if(v14){s1_1=s1;v5=memcmp(s1,_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649C,0x60u)==0;gotoLABEL_20;}v15=v21;v16=v20>>1;_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649=_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649C;do{v5=*v15==(unsigned __int8)*_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649;if(*v15!=(unsigned __int8)*_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649)break;++_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649;++v15;--v16;}while(v16);}else{v5=0;}if(v14){s1_1=s1;LABEL_20:operatordelete(s1_1);}operatordelete(_4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649C);if(LOBYTE(v23[0])<<31)operatordelete(moectf2025______);if(LOBYTE(v25[0])<<31)operatordelete(dest_1);returnv5;}

好的,上面就是隐含了加密,以及密钥(然后会看可以发现是check函数下的伪代码,也就是以后在java层看不到就去看核心的调用函数再从ida分析出来的so文件去定位)

  • 密钥:moectf2025!!!(16位)

  • 加密方式是:SM4-ECB模式(这里又已知了一个加密手段)

  • 已知密文:4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649C0B971BF2EFBCB160E531A646DF7A6AC0B

    exp:

    # SM4-ECB-PKCS7 解密脚本 - 适配你逆向的代码fromcryptography.hazmat.primitives.ciphersimportCipher,algorithms,modesfromcryptography.hazmat.backendsimportdefault_backenddefsm4_ecb_decrypt(key:bytes,cipher_hex:str)->str:# 1. 密钥处理(必须16字节)key=key.encode("utf-8")iflen(key)!=16:raiseValueError("密钥必须是16字节")# 2. 密文十六进制 → 二进制ciphertext=bytes.fromhex(cipher_hex)# 3. SM4-ECB 解密cipher=Cipher(algorithms.SM4(key),modes.ECB(),backend=default_backend())decryptor=cipher.decryptor()padded_plain=decryptor.update(ciphertext)+decryptor.finalize()# 4. PKCS7 去填充(你逆向代码的填充方式)pad_len=padded_plain[-1]plain=padded_plain[:-pad_len]try:returnplain.decode("utf-8")except:returnplain.hex()# ===================== 你的题目数据 =====================KEY="moectf2025!!!!!!"# 题目给的16字节密钥CIPHER_TEXT="4EEB1EEF2914D79BFA8C5006332097ED2EF06C4A59CAE31C827A08D45CC649C0B971BF2EFBCB160E531A646DF7A6AC0B"# 解密result=sm4_ecb_decrypt(KEY,CIPHER_TEXT)print("✅ 解密成功!")print("明文 =",result)

总结

流程就是:1.先看下程序吧,拖入模拟器里面,(因为前面不是遇到了输入用户名和密码的界面嘛,方便理解java代码)

→2.再拖入jadx里面去查找主入口(不一定每个都是mainactivity,向上面就有login为主入口的)

→3.点进去看有无主要的加密逻辑

→4.如果没有说明java层并没有,就是隐藏在so层了,直接解包拿到so文件拖入ida进行分析

http://www.jsqmd.com/news/711923/

相关文章:

  • 2026甘肃亮化工程权威TOP5排行:兰州亮化工程/兰州亮化设计/兰州体育场亮化/兰州体育场泛光照明/兰州商业综合体亮化/选择指南 - 优质品牌商家
  • NDCG@k:推荐系统排序质量评估的核心指标
  • 苹果MacBook Neo与保时捷968 Club Sport:如何让便宜产品变酷炫,成市场新宠?
  • 2026年合肥留学机构测评,最好的口碑好中介如何选 - 速递信息
  • 宜宾宅心装饰2026技术解析:口碑背后的工艺与服务细节 - 优质品牌商家
  • YOLO26电梯内电动车识别检测系统(项目源码+YOLO数据集+模型权重+UI界面+python+深度学习+远程环境部署)
  • Portarium:轻量级本地服务可视化管理的Go语言实现
  • 2026年武汉留学中介机构前十解析,哪家科研服务口碑最好 - 速递信息
  • 2026年3月回收运动木地板品牌推荐,二手运动体育木地板回收/回收运动木地板,回收运动木地板服务联系电话 - 品牌推荐师
  • AI编程助手技术对比与实战应用指南
  • RoPE频谱放大与Transformer位置编码优化实践
  • 5分钟上手!无需API权限的Instagram数据爬虫工具实战指南
  • ncmdump终极指南:3分钟掌握NCM格式解密,解锁网易云音乐播放自由
  • 中位数【C语言】
  • 实际生产开发到底怎么用锁?单体本地锁/数据库锁/Redis分布式锁 真实场景
  • 深入浅出 16.1 例题(二叉树)P4715 P4913
  • 2026年香港留学推荐,学员满意度高的中介机构全面测评 - 速递信息
  • Linux入门】VMware安装CentOS 7超详细图文教程(附常见问题解决)
  • metaRTC8 成功适配 RTOS:开启 MCU/嵌入式实时音视频新时代
  • CUDA应用检查点技术:透明化GPU状态保存与恢复
  • 基于VirtualLab Fusion的微结构仿真设计与加工技术(光栅、超表面、蛾眼结构的仿真与加工技术)课程
  • 如何在雀魂对局中获得AI实时分析:Akagi麻将辅助工具完整指南
  • 多项式优化问题的低秩求解器技术比较与应用
  • 去年春季近2万人参与的AI春训营,正式启航!
  • 宜宾装修公司排行:本土与连锁品牌实力对比解析 - 优质品牌商家
  • 电脑清理与提速
  • 2026年新加坡留学机构全面测评,头部机构性价比高哪家更靠谱 - 速递信息
  • 网易云音乐FLAC无损音乐批量下载:3步轻松获取高品质音乐库
  • AgentFlocks:构建去中心化多智能体协作系统的开源框架实践
  • BP Doctor PRO智能手表评测:血压监测与健康管理