事情是这样的,由于一些原因我需要把Java代码中的一些关键词筛选出来,并将其行号,命中内容等都筛选出来。以此为数据源做后续的扫描识别工作。
分析目标关键词类型,可以分为两类:字符串和库对象引用。
- 字符串样例:
"android.permission.READ_CALENDAR"
- 库对象引用样例
Manifest.permission.CAMERA
所以首先就是把工程中的java文件都读入到内存中,然后按行遍历内容,逐个匹配关键字,当匹配上后,就记录文件、行号以及命中内容。很好,很简单。于是有了下面的代码。
import re
import os
PERMISSION_PATTERN = [r'android.permission.[a-zA-Z]*', r'Manifest.permission.[a-zA-Z]*']
# 获取某目录下所有的java文件路径
def read_all_java(file_dir):
# 读取目录下所有Java的内容
def find_all_permission_statement_place(line):
for p in PERMISSION_PATTERN:
find = re.findall(p, line)
if find:
return find[0]
return None
看起来很不错,我甚至用了个常量组,方便日后的扩展。我真是个小天才。
但是当脚本跑起来后,有几个已知的代码段落没有被识别出来。对于一个想要用脚本偷懒的人来说,漏场景实在是太可怕了,于是赶紧分析被遗漏的场景。经分析,这些被遗漏的场景是由于Java代码格式化时,由于某行代码过长而换行导致的。如下例。
String pe = Manifest.permission
.CAMERA;
这种这行实在是太讨厌了,举一反三,除了这种对象链换行,还有一种字符串换行。所以,如果想用上面的代码段将所有场景都扫描出来,我们需要将折行的代码重新拼接起来。
public static final String WRITE_EXTERNAL_STORAGE = "android.permission" +
".WRITE_EXTERNAL_STORAGE"
经分析,可以将目前两种目标关键字的折行类型分为四种,分别为,“.”行首、“.”行尾、“+”行首、“+”行尾。
// “.”行首
String pe = Manifest.permission
.CAMERA;
// “.”行尾
String pe = Manifest.permission.
CAMERA;
// "+"行首
public static final String WRITE_EXTERNAL_STORAGE = "android.permission"
+ ".WRITE_EXTERNAL_STORAGE"
// "+"行尾
public static final String WRITE_EXTERNAL_STORAGE = "android.permission" +
".WRITE_EXTERNAL_STORAGE"
对于上述四种场景,我们需要对原始数据进行一次清洗。具体实现如下。
def read_file_and_format(file_dir):
"""
读取文件内容,以列表形式返回
会将其因为格式化而造成的".","+"换行恢复
"""
origin_data = read_file(file_dir)
result = []
index = 0
point_cache = None
plus_sign_cache = None
for d in origin_data:
# 行首"."换行处理
if d.strip().startswith("."):
pre_line = result[index - 1]
result[index - 1] = (pre_line + d.strip())
continue
# 行尾"."换行处理
if point_cache:
d = point_cache + d.strip()
point_cache = None
if d.strip().endswith("."):
point_cache = d.replace("\n", "")
continue
# 行首字符串"+"换行处理
if d.strip().startswith("+"):
find = re.findall(r'[\+][\s*]["]', d.strip())
if find:
pre_line = result[index - 1].strip()[:-1]
result[index - 1] = pre_line + d.strip().replace(find[0], "")
continue
# 行尾字符串"+"换行处理
if plus_sign_cache:
d = plus_sign_cache + d.strip()[1:len(d)]
plus_sign_cache = None
if d.strip().endswith("+"):
find = re.findall(r'["][\s*][\+]', d.strip())
if find:
plus_sign_cache = d.strip().replace(find[0], "").replace("\n", "")
continue
index = index + 1
result.append(d.replace("\n", ""))
return result
上述处理很土鳖,就是遍历整体文件,然后按照特征找出对应的行,上下拼接。好歹把功能实现了,其中使用的正则都是我一点一点试出来的,挺费劲,记录一下,以资来者。
这个过程中有个坑,就是习惯了Java的replace支持正则替换,但python的replace不支持,在这里无效定位了半天。
另外,其实聪明如你的小伙伴已经看出来了,我其实想要使用python脚本来把Android权限相关内容提取出来,这块不清楚有没有比较牛的python库可以直接解析Java文件,然后返回python中可以直接调用的类、方法、成员变量等等的数据结构。望大神们清楚地点拨一二,小子不胜感激。