http://zhuoyue360.com/crack/72.html
ApplicationScanner源码阅读
近期有Apk漏洞扫描的需求,参考一下优秀的开源项目
https://github.com/paradiseduo/ApplicationScanner
项目结构
│ .gitignore
│ AppScanner.py
│ LICENSE
│ README.md
│ requirements.txt
│ __init__.py
│
├─lib
│ │ apk.py
│ │ Base.py
│ │ info.py
│ │ ipa.py
│ │ tools.py
│ │ translation.py
│ │ __init__.py
│ │
│ ├─Android
│ │ BackupCheck.py
│ │ BroadcastCheck.py
│ │ ClipboardCheck.py
│ │ DBCheck.py
│ │ DexLoadCheck.py
│ │ EncryptCheck.py
│ │ FFmpegCheck.py
│ │ FragmentCheck.py
│ │ HiddenIntentCheck.py
│ │ IPCheck.py
│ │ JSCheck.py
│ │ LogCheck.py
│ │ PendingIntentCheck.py
│ │ PortCheck.py
│ │ ReadFileCheck.py
│ │ ReflectCheck.py
│ │ ScreenshotCheck.py
│ │ SoCheck.py
│ │ SoLoadCheck.py
│ │ SQLInjectCheck.py
│ │ URLCheck.py
│ │ WebStorageCheck.py
│ │ WebViewCheck.py
│ │ XSSCheck.py
│ │ ZipCheck.py
│ │ __init__.py
│ │
│ └─iOS
│ APICheck.py
│ ARCCheck.py
│ ASLRCheck.py
│ CanaryCheck.py
│ CodeSignCheck.py
│ IBackDoorCheck.py
│ IPCheck.py
│ MallocCheck.py
│ MprotectCheck.py
│ NSLogCheck.py
│ ObscureCheck.py
│ RestrictCheck.py
│ SignatureCheck.py
│ SQLiteCheck.py
│ URLCheck.py
│ WeakCryptCheck.py
│ WeakHashCheck.py
│ WeakRandomCheck.py
│ WebViewCheck.py
│ XcodeGhostCheck.py
│ ZipperDownCheck.py
│ __init__.py
│
└─ThirdTools
apksigner.jar
apktool.jar
1. 入口分析
该项目的启动命令是
python3 AppScanner.py -i test.apk
阅读下Appscanner.py文件. 抽离下关键代码. 主要是调用了apkScan , 进入到lib.apk 看看
from lib.apk import apkScan
def main(argv):
inputfile = ''
save = False
if len(inputfile) > 0:
if not os.path.exists(inputfile):
console.print('File not exist!', style='red bold')
sys.exit(0)
if '.apk' in inputfile:
apkScan(inputfile, save)
elif '.ipa' in inputfile:
ipaScan(inputfile, save)
else:
console.print('Application must be *.apk or *.ipa', style='red bold')
sys.exit(2)
else:
printUse()
sys.exit(2)
2. lib/apk.py文件
这边有个比较有意思的点, 在前面看了他的文件结构,可以知道,他是一个文件,一个检测脚本的. 所以他是怎么实现的呢?
反射手段
scanners = {}
def register(scanner_class):
scanners[scanner_class.__name__] = scanner_class
def scanner(scanner_key):
scanner_class = scanners.get(scanner_key, None)
if scanner_class is None:
return None
return scanner_class
def import_scanners(scanners_imports):
for runner_import in scanners_imports:
__import__(runner_import)
from . import Android
当from . import Android # 执行导入包到 scanners 执行后,会执行__init__.py
在Android/__init__.py 文件中,发现,他通过importlib.import_module 来动态的导入了对象.
import os
import importlib
import logging
from .. import ROOT_PATH, name
logger = logging.getLogger(__name__)
for item in os.listdir(os.path.join(ROOT_PATH, "Android")):
if '__' in item or '.DS_Store' in item:
continue
else:
prefix, suffix = os.path.splitext(item)
if suffix == ".py":
try:
importlib.import_module(f"{name}.Android.{prefix}")
except Exception as e:
logger.exception(e)
紧接着就到了apkScan 函数了
def apkScan(inputfile, save):
console.print('\n[magenta]Unzip apk [/magenta][bold magenta]' + inputfile + '[/bold magenta]')
filePath = inputfile.replace('.apk', '').split('/')[-1] + randomStr(6)
strline = f'java -jar {apktool} d -f {inputfile} -o {filePath} --only-main-classes'
subprocess.Popen(strline, shell=True).communicate()
console.print('[bold green]Finish[/bold green]')
filePath = os.path.abspath(filePath)
try:
apkInfo(filePath)
permissionAndExport(filePath)
appSign(inputfile)
fingerPrint(filePath)
for key in scanners.keys():
// 动态扫描的关键.
c = scanner(key)
if c:
c(filePath).scan()
except:
print(traceback.format_exc())
if not save:
console.print('\n[bold magenta]Clean cache...[/bold magenta]')
shutil.rmtree(filePath)
console.print('[bold green]Finish[/bold green]')
动态执行脚本的关键代码如下:
for key in scanners.keys():
// 动态扫描的关键.
c = scanner(key)
if c:
c(filePath).scan()
那么是哪里对scanners 变量进行操作了呢?
.\lib\Android\BackupCheck.py
原来是在导入包的时候就已经注册了函数,把各个类给了scanners变量. 妙哉妙哉~. 最后在循环一下,调用scan方法. 如此这般.
import xml.dom.minidom
from ..Base import Base
from ..info import Info
from ..apk import register
import os
from lib.translation import *
class BackupCheck(Base):
def scan(self):
set_values_for_key(key='BACKUPCHECKTITLE', zh='应用数据任意备份风险检测', en='Application data arbitrary backup risk detection')
set_values_for_key(key='BACKUPCHECHINFO', zh='检测App是否存在应用数据被任意备份的风险', en='Detect whether the app has the risk of app data being arbitrarily backed up')
TITLE = get_value('BACKUPCHECKTITLE')
LEVEL = 2
INFO = get_value('BACKUPCHECHINFO')
strline = 'find ' + self.appPath +' -name AndroidManifest.xml | grep -v "/original/"'
arr = os.popen(strline).readlines()
for item in arr:
tree = xml.dom.minidom.parse(item[:-1])
root = tree.documentElement
application = root.getElementsByTagName('application')
result = ''
for a in application:
if a.getAttribute('android:allowBackup') == 'true':
result = 'android:allowBackup = true'
Info(key=self.__class__, title=TITLE, level=LEVEL, info=INFO, result=result).description()
register(BackupCheck)
3. 总结
本次分析没有分析其如何对apk进行漏洞扫描的, 原因是现在大部分的apk都进行了加固,这种扫描有时候的意义并不是很大的.
如果要做漏洞扫描的话,小编认为可以这么做:
- 动态dump dex
- 在把dump下来的dex进行动态扫描.
这样子可以尽可能的扫描的全一点吧~
本次主要还是学习该项目对许许多多的脚本的时候,如何更加规范的编写代码,防止代码像个狗屎一样(目前面临的问题.)
再会~~
|