当前位置:   article > 正文

AKP分析(基于取证)_apk查壳工具

apk查壳工具

APP基础

格式:

*.IPA IOS系统的app文件格式

*.APK 安卓系统的app文件格式

APK静态分析

加固(壳):

目的:

防止APK被逆向,防篡改,实现数据保护

种类:

APK加壳的中了非常多,我在取证过程中所见到的加壳种类有UPX和pyintaller的加壳方式

脱壳:

一般我们用雷电挂载APK时可以直接使用雷电自带的脱壳来脱壳之后反编译。但是有些恶意文件不能用雷电来挂载,这个时候我们就要手动查壳

查壳工具:

Detect It Easy3.7(之前老的版本可能查不到pyinstaller这个加壳方式)

PEiD:偶尔用,效果不是很好

当然可以用沙箱的方法看一下他是不是有壳

脱壳工具:

知道是什么壳加固了的话,就可以对症下药了

如果是UPX壳的话就用UPX tool来脱

或者360壳,使用取证工具来脱

如果是pyinstaller壳的话用脚本来脱,将脚本和恶意文件放到一个文件夹下面,用cmd跑,会得到一个文件夹,文件夹里的文件不带pyc后缀,要自己加上,然后这些文件也缺少文件头,还要自己补充

然后可以使用在线pyc反编译

pyc反编译 - 爱资料工具 (toolnb.com)

  1. """
  2. PyInstaller Extractor v1.9 (Supports pyinstaller 3.3, 3.2, 3.1, 3.0, 2.1, 2.0)
  3. Author : Extreme Coders
  4. E-mail : extremecoders(at)hotmail(dot)com
  5. Web : https://0xec.blogspot.com
  6. Date : 29-November-2017
  7. Url : https://sourceforge.net/projects/pyinstallerextractor/
  8. For any suggestions, leave a comment on
  9. https://forum.tuts4you.com/topic/34455-pyinstaller-extractor/
  10. This script extracts a pyinstaller generated executable file.
  11. Pyinstaller installation is not needed. The script has it all.
  12. For best results, it is recommended to run this script in the
  13. same version of python as was used to create the executable.
  14. This is just to prevent unmarshalling errors(if any) while
  15. extracting the PYZ archive.
  16. Usage : Just copy this script to the directory where your exe resides
  17. and run the script with the exe file name as a parameter
  18. C:\path\to\exe\>python pyinstxtractor.py <filename>
  19. $ /path/to/exe/python pyinstxtractor.py <filename>
  20. Licensed under GNU General Public License (GPL) v3.
  21. You are free to modify this source.
  22. CHANGELOG
  23. ================================================
  24. Version 1.1 (Jan 28, 2014)
  25. -------------------------------------------------
  26. - First Release
  27. - Supports only pyinstaller 2.0
  28. Version 1.2 (Sept 12, 2015)
  29. -------------------------------------------------
  30. - Added support for pyinstaller 2.1 and 3.0 dev
  31. - Cleaned up code
  32. - Script is now more verbose
  33. - Executable extracted within a dedicated sub-directory
  34. (Support for pyinstaller 3.0 dev is experimental)
  35. Version 1.3 (Dec 12, 2015)
  36. -------------------------------------------------
  37. - Added support for pyinstaller 3.0 final
  38. - Script is compatible with both python 2.x & 3.x (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)
  39. Version 1.4 (Jan 19, 2016)
  40. -------------------------------------------------
  41. - Fixed a bug when writing pyc files >= version 3.3 (Thanks to Daniello Alto: https://github.com/Djamana)
  42. Version 1.5 (March 1, 2016)
  43. -------------------------------------------------
  44. - Added support for pyinstaller 3.1 (Thanks to Berwyn Hoyt for reporting)
  45. Version 1.6 (Sept 5, 2016)
  46. -------------------------------------------------
  47. - Added support for pyinstaller 3.2
  48. - Extractor will use a random name while extracting unnamed files.
  49. - For encrypted pyz archives it will dump the contents as is. Previously, the tool would fail.
  50. Version 1.7 (March 13, 2017)
  51. -------------------------------------------------
  52. - Made the script compatible with python 2.6 (Thanks to Ross for reporting)
  53. Version 1.8 (April 28, 2017)
  54. -------------------------------------------------
  55. - Support for sub-directories in .pyz files (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)
  56. Version 1.9 (November 29, 2017)
  57. -------------------------------------------------
  58. - Added support for pyinstaller 3.3
  59. - Display the scripts which are run at entry (Thanks to Michael Gillespie @ malwarehunterteam for the feature request)
  60. """
  61. from __future__ import print_function
  62. import os
  63. import struct
  64. import marshal
  65. import zlib
  66. import sys
  67. import imp
  68. import types
  69. from uuid import uuid4 as uniquename
  70. class CTOCEntry:
  71. def __init__(self, position, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name):
  72. self.position = position
  73. self.cmprsdDataSize = cmprsdDataSize
  74. self.uncmprsdDataSize = uncmprsdDataSize
  75. self.cmprsFlag = cmprsFlag
  76. self.typeCmprsData = typeCmprsData
  77. self.name = name
  78. class PyInstArchive:
  79. PYINST20_COOKIE_SIZE = 24 # For pyinstaller 2.0
  80. PYINST21_COOKIE_SIZE = 24 + 64 # For pyinstaller 2.1+
  81. MAGIC = b'MEI\014\013\012\013\016' # Magic number which identifies pyinstaller
  82. def __init__(self, path):
  83. self.filePath = path
  84. def open(self):
  85. try:
  86. self.fPtr = open(self.filePath, 'rb')
  87. self.fileSize = os.stat(self.filePath).st_size
  88. except:
  89. print('[*] Error: Could not open {0}'.format(self.filePath))
  90. return False
  91. return True
  92. def close(self):
  93. try:
  94. self.fPtr.close()
  95. except:
  96. pass
  97. def checkFile(self):
  98. print('[*] Processing {0}'.format(self.filePath))
  99. # Check if it is a 2.0 archive
  100. self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
  101. magicFromFile = self.fPtr.read(len(self.MAGIC))
  102. if magicFromFile == self.MAGIC:
  103. self.pyinstVer = 20 # pyinstaller 2.0
  104. print('[*] Pyinstaller version: 2.0')
  105. return True
  106. # Check for pyinstaller 2.1+ before bailing out
  107. self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
  108. magicFromFile = self.fPtr.read(len(self.MAGIC))
  109. if magicFromFile == self.MAGIC:
  110. print('[*] Pyinstaller version: 2.1+')
  111. self.pyinstVer = 21 # pyinstaller 2.1+
  112. return True
  113. print('[*] Error : Unsupported pyinstaller version or not a pyinstaller archive')
  114. return False
  115. def getCArchiveInfo(self):
  116. try:
  117. if self.pyinstVer == 20:
  118. self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
  119. # Read CArchive cookie
  120. (magic, lengthofPackage, toc, tocLen, self.pyver) = \
  121. struct.unpack('!8siiii', self.fPtr.read(self.PYINST20_COOKIE_SIZE))
  122. elif self.pyinstVer == 21:
  123. self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
  124. # Read CArchive cookie
  125. (magic, lengthofPackage, toc, tocLen, self.pyver, pylibname) = \
  126. struct.unpack('!8siiii64s', self.fPtr.read(self.PYINST21_COOKIE_SIZE))
  127. except:
  128. print('[*] Error : The file is not a pyinstaller archive')
  129. return False
  130. print('[*] Python version: {0}'.format(self.pyver))
  131. # Overlay is the data appended at the end of the PE
  132. self.overlaySize = lengthofPackage
  133. self.overlayPos = self.fileSize - self.overlaySize
  134. self.tableOfContentsPos = self.overlayPos + toc
  135. self.tableOfContentsSize = tocLen
  136. print('[*] Length of package: {0} bytes'.format(self.overlaySize))
  137. return True
  138. def parseTOC(self):
  139. # Go to the table of contents
  140. self.fPtr.seek(self.tableOfContentsPos, os.SEEK_SET)
  141. self.tocList = []
  142. parsedLen = 0
  143. # Parse table of contents
  144. while parsedLen < self.tableOfContentsSize:
  145. (entrySize, ) = struct.unpack('!i', self.fPtr.read(4))
  146. nameLen = struct.calcsize('!iiiiBc')
  147. (entryPos, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name) = \
  148. struct.unpack( \
  149. '!iiiBc{0}s'.format(entrySize - nameLen), \
  150. self.fPtr.read(entrySize - 4))
  151. name = name.decode('utf-8').rstrip('\0')
  152. if len(name) == 0:
  153. name = str(uniquename())
  154. print('[!] Warning: Found an unamed file in CArchive. Using random name {0}'.format(name))
  155. self.tocList.append( \
  156. CTOCEntry( \
  157. self.overlayPos + entryPos, \
  158. cmprsdDataSize, \
  159. uncmprsdDataSize, \
  160. cmprsFlag, \
  161. typeCmprsData, \
  162. name \
  163. ))
  164. parsedLen += entrySize
  165. print('[*] Found {0} files in CArchive'.format(len(self.tocList)))
  166. def extractFiles(self):
  167. print('[*] Beginning extraction...please standby')
  168. extractionDir = os.path.join(os.getcwd(), os.path.basename(self.filePath) + '_extracted')
  169. if not os.path.exists(extractionDir):
  170. os.mkdir(extractionDir)
  171. os.chdir(extractionDir)
  172. for entry in self.tocList:
  173. basePath = os.path.dirname(entry.name)
  174. if basePath != '':
  175. # Check if path exists, create if not
  176. if not os.path.exists(basePath):
  177. os.makedirs(basePath)
  178. self.fPtr.seek(entry.position, os.SEEK_SET)
  179. data = self.fPtr.read(entry.cmprsdDataSize)
  180. if entry.cmprsFlag == 1:
  181. data = zlib.decompress(data)
  182. # Malware may tamper with the uncompressed size
  183. # Comment out the assertion in such a case
  184. assert len(data) == entry.uncmprsdDataSize # Sanity Check
  185. with open(entry.name, 'wb') as f:
  186. f.write(data)
  187. if entry.typeCmprsData == b's':
  188. print('[+] Possible entry point: {0}'.format(entry.name))
  189. elif entry.typeCmprsData == b'z' or entry.typeCmprsData == b'Z':
  190. self._extractPyz(entry.name)
  191. def _extractPyz(self, name):
  192. dirName = name + '_extracted'
  193. # Create a directory for the contents of the pyz
  194. if not os.path.exists(dirName):
  195. os.mkdir(dirName)
  196. with open(name, 'rb') as f:
  197. pyzMagic = f.read(4)
  198. assert pyzMagic == b'PYZ\0' # Sanity Check
  199. pycHeader = f.read(4) # Python magic value
  200. if imp.get_magic() != pycHeader:
  201. print('[!] Warning: The script is running in a different python version than the one used to build the executable')
  202. print(' Run this script in Python{0} to prevent extraction errors(if any) during unmarshalling'.format(self.pyver))
  203. (tocPosition, ) = struct.unpack('!i', f.read(4))
  204. f.seek(tocPosition, os.SEEK_SET)
  205. try:
  206. toc = marshal.load(f)
  207. except:
  208. print('[!] Unmarshalling FAILED. Cannot extract {0}. Extracting remaining files.'.format(name))
  209. return
  210. print('[*] Found {0} files in PYZ archive'.format(len(toc)))
  211. # From pyinstaller 3.1+ toc is a list of tuples
  212. if type(toc) == list:
  213. toc = dict(toc)
  214. for key in toc.keys():
  215. (ispkg, pos, length) = toc[key]
  216. f.seek(pos, os.SEEK_SET)
  217. fileName = key
  218. try:
  219. # for Python > 3.3 some keys are bytes object some are str object
  220. fileName = key.decode('utf-8')
  221. except:
  222. pass
  223. # Make sure destination directory exists, ensuring we keep inside dirName
  224. destName = os.path.join(dirName, fileName.replace("..", "__"))
  225. destDirName = os.path.dirname(destName)
  226. if not os.path.exists(destDirName):
  227. os.makedirs(destDirName)
  228. try:
  229. data = f.read(length)
  230. data = zlib.decompress(data)
  231. except:
  232. print('[!] Error: Failed to decompress {0}, probably encrypted. Extracting as is.'.format(fileName))
  233. open(destName + '.pyc.encrypted', 'wb').write(data)
  234. continue
  235. with open(destName + '.pyc', 'wb') as pycFile:
  236. pycFile.write(pycHeader) # Write pyc magic
  237. pycFile.write(b'\0' * 4) # Write timestamp
  238. if self.pyver >= 33:
  239. pycFile.write(b'\0' * 4) # Size parameter added in Python 3.3
  240. pycFile.write(data)
  241. def main():
  242. if len(sys.argv) < 2:
  243. print('[*] Usage: pyinstxtractor.py <filename>')
  244. else:
  245. arch = PyInstArchive(sys.argv[1])
  246. if arch.open():
  247. if arch.checkFile():
  248. if arch.getCArchiveInfo():
  249. arch.parseTOC()
  250. arch.extractFiles()
  251. arch.close()
  252. print('[*] Successfully extracted pyinstaller archive: {0}'.format(sys.argv[1]))
  253. print('')
  254. print('You can now use a python decompiler on the pyc files within the extracted directory')
  255. return
  256. arch.close()
  257. if __name__ == '__main__':
  258. main()

内部结构:

APK本质是一个压缩包,可以修改为.zip然后解压看到里面的内部文件

AndroidManifest.xml全局清单文件(最重要)

使用雷电我们是可以直接看到的这些内容

内含:APK安装包名、版本、SDK版本、APP运行权限、Activity主入口函数,第三方SDK调用信息

classes.dex(在虚拟机上可执行文件)Bytecode程序代码主体

是我们逆向分析的主要对象是java源码编译生成的java字节文件(是java文件经过jdk编译,再经dex编译生成classes.dex)。可查看域名,IP,加解密算法之类的信息

查看工具:雷电、JEB(看不懂的代码可以使用tab键转换为java语言)、ida等反编译工具

分析方法:先找主程序/入口程序/主函数,然后顺着往下看(合理利用查找)

META-INF(签名信息)

APK中的证书公钥、有效日期、签名以及证书的MD5、SHA-1/256、指纹信息、数字签名(不同的程序可能名字相同但数字签名不同)

resources.arsc(二进制索引文件)

图片,颜色等现在这个文件里面存储,然后在映射道Android的R文件中,形成ID,在使用程序资源是直接就可以通过ID调用

Assets(资源文件夹)

lib(存放so文件)

存放第三方库文件,这些so文件一般都是使用C或C++编写,单靠JAVA很难实现高性能,会依赖SO文件,一般一些隐藏的加密文件都会用so文件封装

根据CPU性能架构,lib分为四种分别是ARM(高通骁龙、麒麟基本都是)、ARM-V7、MIPS、x86(桌面笔记本CPU一般使用),对应目录为armeabi、armeabi-v7a、mips、x86

一般越好的程序他的架构越多,能在各种手机上面使用,甚至电脑

分析方法

混淆市代码加固方式的解决

通过修该代码函数的名字起到影响查找函数的作用,比如把函数名字改成abcdef什么的

先找主函数,可以找LAUNCHER,LAUNCHER上面那个activity名字就是主函数,然后一步步找主函数页面,找到主函数只有再找oncreate

正则表达式搜索

使用魔盾安全可以直接看到一些重要信息

APK动态分析

APK抓包概述

一般抓包使用https抓包

抓包工具

代理抓包工具(小而美):FIddler、Charles等。可以解密https的,tcp之上的都可以解析

非代理抓包:wireshark、httpdebugger

取证抓包工具:应用程序检测大师系统、雷电

FLDDLER抓包

首先下载并配置了flddler的抓包工具

端口写一个不会冲突的端口

然后我们要配置我们的雷电模拟器(注意这里必须要用雷电九,雷电四不能设置WiFi代理)

点击WiFi的齿轮设置手动代理

代理服务器主机名是我们电脑的wife地址

设置完代理之后我们用手机访问就是可以发现是能抓到报的了

确实抓到了

若要解密https流,我们要重新设置

双方都要安装假证书

两者都安装好之后,查看https的官网就可以看到了

这两个图片都有

adb.exe命令

adb.exe在雷电的文件夹里面,使用要使用cmd

使用方法:

先使用adb devices命令测试链接(出现xxx device表示测试成功可以通讯,不成功可以考虑usb模式是否打开)

在手机上打开要测试的软件程序

adb shell dumpsys window | findstr mCurrentFocus(mCurrentFocus表示鼠标当前光标位置,也就是打开的软件) 获取当前窗口特征,比如包名

adb shell ps 获取进程信息,可以找进程名

adb shell pm path 包名 获取安装路径

adb pull + 路径名,可以讲文件备份拉出来,不指定路径的话就默认存储在雷电文件夹下面

安卓文件目录查看:

APP安装包: /data/app/目录

APP存储位置: /data/data/目录

vmdk文件取证

有的时候给的不是E01或者DD镜像格式,而是给的vmdk格式的镜像,一般会给你三个:data.vmdk system.vmdk sdcard.vmdk文件,这种用火眼是不能分析的

解析方法:

手机大师解析

模拟器镜像替换解析:在雷电文件夹下的vms文件里面替换

取证大师解析

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/638080
推荐阅读
相关标签
  

闽ICP备14008679号