Android漏洞扫描工具Code Arbiter

目前Android应用代码漏洞扫描工具种类繁多,效果良莠不齐,这些工具有一个共同的特点,都是在应用打包完成后对应用进行解包扫描。这种扫描有非常明显的缺点,扫描周期较长,不能向开发者实时反馈代码中存在的安全问题,并且对于问题代码的定位需要手动搜索匹配源码,这样就更不利于开发者对问题代码进行及时的修改。Code Arbiter正是为解决上述两个问题而开发的,专门对Android Studio中的源码进行安全扫描。

为实现对Android Studio中的源码进行扫描,最方便的方式便是将扫描工具以IDE插件的形式进行工作。此时一个很自然的想法便是从头构建一个Android Studio插件,但是进行仔细的评估后会发现,这样做难度并不小: 1. 工作量大,许多知识需要学习,如IDE开放API接口、插件UI构建等,同时许多底层模块需要从头构建; 2. 插件的稳定性、检测问题的准确性上都不一定能够达到已有开源工具的效果。

因此我们转而考虑在已有漏洞检测插件的基础上进行扩展,以满足需求。经过调研,最终入围的两款检测插件是PMD和FindBugs,其中PMD是对Java源码进行扫描,而FindBugs则是对Java源码编译后的class文件进行扫描。考虑到可扩展性及检测的准确性,最终选定了FindBugs。FindBugs是一个静态分析工具,它检查类或者JAR文件,将字节码与一组缺陷模式进行对比来发现可能的问题,可以以独立的JAR包形式运行,也可以作为集成开发工具的插件形式存在。

扩展优化

那么,怎么扩展FindBugs呢?调研发现FindBugs插件具有着极强的可扩展性,只需要将扩展的JAR包导入FindBugs插件,重启,即可完成相关功能的扩展。安装JAR包示意图如下所示。

安装jar包

下面的问题是如何构建可安装的JAR包。继续调研,发现FindBugs有一款专门对安全问题进行检测的扩展插件Find Security Bugs,该插件主要用于对Web安全问题进行检测,也有极少对Android相关安全问题的检测规则。考虑以下几个原因,需要对该插件的源码进行重构。 1. 对Android安全问题的检测太少,只包含外部文件使用、Webview、Broadcast使用等寥寥几项; 2. 检测的细粒度上考虑不够完全,会造成大量的误报,无法满足检测精度的要求; 3. 检测问题的上报只支持英文模式,且问题展示的逻辑性不够严谨,不便于开发者进行问题排查。

基于以上三个原因,我们需要对Find Security Bugs的源码进行重写、优化,通过增加检测项来检测尽可能多的安全问题,通过优化检测规则来减少检测的误报,问题展示使用中文进行描述,同时优化问题描述的逻辑性,使得开发者能够更易理解并修改相关问题,至此插件实现及优化的方案确定。

FindBugs检测的是class文件,因此当待检测的源码未生成编译文件时,FindBugs会先将源码编译生成.class文件,然后对这个class文件进行分析。FindBugs会完成对class文件的自动建模,在此模型的基础上对代码进行分析。按照在实际编写检测代码过程中的总结,把检测的实现方式分成四种方式,下面分别进行介绍。

2.1 逐行检查

逐行检查主要是针对代码中使用的一些不安全方法或参数进行检测,其实现方式是重写sawOpcode()方法,下面以Android中使用外部存储问题作为示例进行讲解。

Android中获取外部存储文件夹地址的方法主要包括下面这些方法:

getExternalCacheDir()
getExternalCacheDirs()
getExternalFilesDir()
getExternalFilesDirs()
getExternalMediaDirs()
Environment.getExternalStorageDirectory()
Environment.getExternalStoragePublicDirectory()

检测的方式便是,如果发现存在该方法的调用,则作为一个问题进行上报,实现完整代码如下所示:

public class ExternalFileAccessDetector extends OpcodeStackDetector {private static final String ANDROID_EXTERNAL_FILE_ACCESS_TYPE = "ANDROID_EXTERNAL_FILE_ACCESS";private BugReporter bugReporter;public ExternalFileAccessDetector(BugReporter bugReporter) {this.bugReporter = bugReporter;}@Overridepublic void sawOpcode(int seen) {//printOpCode(seen);if (seen == Constants.INVOKEVIRTUAL && (getNameConstantOperand().equals("getExternalCacheDir") ||getNameConstantOperand().equals("getExternalCacheDirs") ||getNameConstantOperand().equals("getExternalFilesDir") ||getNameConstantOperand().equals("getExternalFilesDirs") ||getNameConstantOperand().equals("getExternalMediaDirs"))) {
// System.out.println(getSigConstantOperand());bugReporter.reportBug(new BugInstance(this, ANDROID_EXTERNAL_FILE_ACCESS_TYPE, Priorities.NORMAL_PRIORITY).addClass(this).addMethod(this).addSourceLine(this));}else if(seen == Constants.INVOKESTATIC && getClassConstantOperand().equals("android/os/Environment") && (getNameConstantOperand().equals("getExternalStorageDirectory") || getNameConstantOperand().equals("getExternalStoragePublicDirectory"))) {bugReporter.reportBug(new BugInstance(this, ANDROID_EXTERNAL_FILE_ACCESS_TYPE, Priorities.NORMAL_PRIORITY).addClass(this).addMethod(this).addSourceLine(this));}}
}

该类的实现是继承OpcodeStackDetector类,是FindBugs中的一个抽象类,封装了对于获取代码特定参数的方法调用。sawOpcode方法参数可以理解为待检测代码行的行号,通过printOpCode(seen)可以打印该代码行的具体内容。Constants.INVOKEVIRTUAL表示该行调用类的实例方法,Constants.INVOKESTATIC表示调用类的静态方法。getNameConstantOperand方法表示获取被调用方法的名称,getClassConstantOperand方法表示获取调用类的名称,getSigConstantOperand方法表示获取方法的所有参数。bugReporter.reportBug用于上报检测到的漏洞信息,其中BugInstance的三个参数分别表示:检测器、漏洞类型、漏洞等级,其中漏洞等级分为五个级别,如下表所示:

名称参数含义
HIGH_PRIORITY1高危风险
NORMAL_PRIORITY2中危风险
LOW_PRIORITY3低危风险
EXP_PRIORITY4安全提醒
IGNORE_PRIORITY5可忽略风险

addClass、addMethod、addSourceLine用于指定该漏洞所在的类、方法、行,方便报告漏洞时定位关键代码。

2.2 逐方法检查

逐方法检查首先获取待检测类的所有内容,然后对类中的方法进行逐个检查,多用于对方法体进行检测,其实现的方法主要是通过重写visitClassContext方法,下面以对Android TrustManager的空实现的检测为例进行说明。 TrustManager的空实现,主要是指对于检测Server端证书是否可信的方法checkServerTrusted,是否是空实现。下面展示问题代码,如果是空实现那么将导致客户端接收任意证书,从而造成加密后的HTTPS消息被中间人解密。

@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}

检测的方式是通过遍历类中的所有方法,找到checkServerTrusted方法,对方法整体进行检测,确定其是否为空实现,部分代码如下所示:

public class WeakTrustManagerDetector implements Detector {
...
public WeakTrustManagerDetector(BugReporter bugReporter) {this.bugReporter = bugReporter;}@Overridepublic void visitClassContext(ClassContext classContext) {JavaClass javaClass = classContext.getJavaClass();//The class extends X509TrustManagerboolean isTrustManager = InterfaceUtils.isSubtype(javaClass,"javax.net.ssl.X509TrustManager");boolean isHostnameVerifier = InterfaceUtils.isSubtype(javaClass,"javax.net.ssl.HostnameVerifier");// if (!isTrustManager && !isHostnameVerifier) return;if (!isTrustManager && !isHostnameVerifier){for (Method m : javaClass.getMethods()) {allow_All_Hostname_Verify(classContext, javaClass, m);}}Method[] methodList = javaClass.getMethods();for (Method m : methodList) {MethodGen methodGen = classContext.getMethodGen(m);if (DEBUG) System.out.println(">>> Method: " + m.getName());if (isTrustManager &&(m.getName().equals("checkClientTrusted") ||m.getName().equals("checkServerTrusted") ||m.getName().equals("getAcceptedIssuers"))) {if(isEmptyImplementation(methodGen)) {bugReporter.reportBug(new BugInstance(this, WEAK_TRUST_MANAGER_TYPE, Priorities.NORMAL_PRIORITY).addClassAndMethod(javaClass, m));}
......

classContext.getJavaClass用于获取整个类的所有内容;javaClass.getMethods用于获取该类中的所有方法,以一个方法列表的形式返回;classContext.getMethodGen用于获取该方法的内容;isEmptyImplementation将方法的内容导入该函数进行检测,用于确定方法是否是空实现,该方法的代码如下所示:

private boolean isEmptyImplementation(MethodGen methodGen){boolean invokeInst = false;boolean loadField = false;for (Iterator itIns = methodGen.getInstructionList().iterator();itIns.hasNext();) {Instruction inst = ((InstructionHandle) itIns.next()).getInstruction();if (DEBUG)System.out.println(inst.toString(true));if (inst instanceof InvokeInstruction) {invokeInst = true;}if (inst instanceof GETFIELD) {loadField = true;}}return !invokeInst && !loadField;
}

该方法主要用于检测方法中是否包含方法调用、域操作,如果没有包含则认为是一个空实现的方法。因此该方法对于只包含 return true/false 语句的方法体同样认为是一个空实现。

2.3 污点分析

数据流分析主要用于分析特定方法加载的参数是否能够被用户控制,即进行污点分析。做污点分析首先需要定义污染源(source点),污染源可以理解为能够被用户控制的输入数据,这里定义的Android污染源主要包括用户的输入、Intent传入的数据,下面展示定义的部分污染源(source点):

- EditText
android/widget/EditText.getText()Landroid/text/Editable;:TAINTED
- Intent
android/content/Intent.getAction()Ljava/lang/String;:TAINTED
android/content/Intent.getStringExtra(Ljava/lang/String;)Ljava/lang/String;:TAINTED
......
- Bundle
android/os/Bundle.get(Ljava/lang/String;)Ljava/lang/Object;:TAINTED
android/os/Bundle.getString(Ljava/lang/String;)Ljava/lang/String;:TAINTED
......

定义好污染源后就需要确定污染的触发点(sink点),可以理解为会触发危险操作的函数。定义sink点的方式有两种,一种是直接从文件中导入,以命令注入为示例,代码如下:

public class CommandInjectionDetector extends BasicInjectionDetector {public CommandInjectionDetector(BugReporter bugReporter) {super(bugReporter);loadConfiguredSinks("command.txt", "COMMAND_INJECTION");}

从代码中可以清楚的看到其导入方式是继承BasicInjectionDetector类,然后再该类的构造方法中通过loadConfiguredSinks方法,导入包含sink点的文件,下面展示该示例文件中的内容:

java/lang/Runtime.exec(Ljava/lang/String;)Ljava/lang/Process;:0
java/lang/Runtime.exec([Ljava/lang/String;)Ljava/lang/Process;:0
java/lang/Runtime.exec(Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Process;:0,1
java/lang/Runtime.exec([Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Process;:0,1
java/lang/Runtime.exec(Ljava/lang/String;[Ljava/lang/String;Ljava/io/File;)Ljava/lang/Process;:1,2
java/lang/Runtime.exec([Ljava/lang/String;[Ljava/lang/String;Ljava/io/File;)Ljava/lang/Process;:1,2
java/lang/ProcessBuilder.<init>([Ljava/lang/String;)V:0
java/lang/ProcessBuilder.<init>(Ljava/util/List;)V:0
java/lang/ProcessBuilder.command([Ljava/lang/String;)Ljava/lang/ProcessBuilder;:0
java/lang/ProcessBuilder.command(Ljava/util/List;)Ljava/lang/ProcessBuilder;:0
dalvik/system/DexClassLoader.loadClass(Ljava/lang/String;)Ljava/lang/Class;:0

另一种是自定义导入,其实现是通过覆盖BasicInjectionDetector类中的getInjectionPoint方法,以WebView.loadurl方法为例,示例代码如下所示:

@Overrideprotected InjectionPoint getInjectionPoint(InvokeInstruction invoke, ConstantPoolGen cpg, InstructionHandle handle) {assert invoke != null && cpg != null;String method = invoke.getMethodName(cpg);String sig    = invoke.getSignature(cpg);
// System.out.println(invoke.getClassName(cpg));if(sig.contains("Ljava/lang/String;")) {if("loadUrl".equals(method)){if(sig.contains("Ljava/util/Map;")){return new InjectionPoint(new int[]{1}, WEBVIEW_LOAD_DATA_URL_TYPE);}else{return new InjectionPoint(new int[]{0}, WEBVIEW_LOAD_DATA_URL_TYPE);}}else if("loadData".equals(method)){return new InjectionPoint(new int[]{2}, WEBVIEW_LOAD_DATA_URL_TYPE);}else if("loadDataWithBaseURL".equals(method)){//BUGreturn new InjectionPoint(new int[]{4}, WEBVIEW_LOAD_DATA_URL_TYPE);}}return InjectionPoint.NONE;}

通过实例化InjectionPoint类构造新的sink点,其构造方法中的第一个参数表示该方法接收污染数据参数的位置,如方法为webView.loadUrl(url),其第一个参数就是new int[]{0},其它的以此类推。

上报发现漏洞的情况,则通过覆盖getPriorityFromTaintFrame方法的实现,示例代码如下所示:

@Overrideprotected int getPriorityFromTaintFrame(TaintFrame fact, int offset)throws DataflowAnalysisException {Taint stringValue = fact.getStackValue(offset);
// System.out.println(stringValue.getConstantValue());if (stringValue.isTainted() || stringValue.isUnknown()) {return Priorities.NORMAL_PRIORITY;} else {return Priorities.IGNORE_PRIORITY;}}

通过fact.getStackValue获取检测的函数变量,如果该变量被污染(isTainted)或 变量是否被污染未知(但是是可控制变量),那么作为一个中危风险(Priorities.NORMAL_PRIORITY)进行上报,其它的情况则上报为可忽略风险(Priorities.IGNORE_PRIORITY)。

2.4 自定义代码检测

自定义代码检测实现的前半部分同2.2的逐方法检测类似,均是获取类的内容,然后遍历所有的方法,对方法的内容进行检测,但是在具体代码检测实现上是通过自定义分析进行。目前自定义检测只应用到Android中本地拒绝服务的检测。本地拒绝服务的被触发的重要原因在于对通过Intent获取的参数未进行异常捕获,因此检测实现的方式便是检测获取参数的代码行是否被try catch包裹(这个存在误差,待改进)。对于其代码分析,不能使用FindBugs模型进行分析,而是使用最原始的class代码进行分析,原始class代码的形式通过javap命令进行查看,下图展示示例代码。

class示例代码

对原始class文件进行分析存在的缺陷是无法定位具体的代码行,那么在进行问题上报时无法将问题定位到代码行,因此第一步需要在原有模型的基础上对所有包含Intent获取参数的方法的位置存储到一个Map结构中,方便后面对方法的定位,代码实现如下所示,获取方法所在的行,然后以方法名作为Key值,以代码行相关信息作为Value值,存储到Map中。

private Map<String, List<Location>> get_line_location(Method m, ClassContext classContext){HashMap<String, List<Location>> all_line_location = new HashMap<>();ConstantPoolGen cpg = classContext.getConstantPoolGen();CFG cfg = null;try {cfg = classContext.getCFG(m);} catch (CFGBuilderException e) {e.printStackTrace();return all_line_location;}for (Iterator<Location> i = cfg.locationIterator(); i.hasNext(); ) {Location loc = i.next();Instruction inst = loc.getHandle().getInstruction();if(inst instanceof INVOKEVIRTUAL) {INVOKEVIRTUAL invoke = (INVOKEVIRTUAL) inst;if(all_line_location.containsKey(invoke.getMethodName(cpg))){all_line_location.get(invoke.getMethodName(cpg)).add(loc);}else {LinkedList<Location> loc_list = new LinkedList<>();loc_list.add(loc);all_line_location.put(invoke.getMethodName(cpg), loc_list);}
// }}}return all_line_location;}

之后获取Exception包裹的范围,FindBugs中包含对Exception的建模,因此能够通过其模型能够直接获取其范围并存储到一个列表中,代码如下所示,其中exceptionTable[i].getStartPC用于获取try catch 的起始代码行,exceptionTable[i].getEndPC用于获取try catch 的结束代码行。

public int[] getExceptionScope(){try {CodeException[] exceptionTable = this.code.getExceptionTable();int[] exception_scop = new int[exceptionTable.length * 2];for (int i = 0; i < exceptionTable.length; i++) {exception_scop[i * 2] = exceptionTable[i].getStartPC();exception_scop[i * 2 + 1] = exceptionTable[i].getEndPC();}return exception_scop;}catch (Exception e){}return new int[0];}

在对代码进行逐行检查时,因为使用的是最原始class文件形式,因此需要限定其遍历的范围,限定的方式是通过代码的行号,即上图中每行代码的第一个数值。首先需要获取代码总行数的大小,获取的方式便是解析FindBugs建模后的第一行代码,找到关键词code-length后面的数值,即为代码的行数,解析代码如下所示:

public int get_Code_Length(String firstLineCode){try{String[] split1 = firstLineCode.split("code_length");
// System.out.println(split1[split1.length-1]);byte[] code_length_bytes = split1[split1.length-1].getBytes();byte[] new_code_bytes = new byte[code_length_bytes.length];for(int i=0; i<code_length_bytes.length; i++){
// System.out.println();if(code_length_bytes[i]<48 || code_length_bytes[i]>57){new_code_bytes[i] = 32;}else{new_code_bytes[i] = code_length_bytes[i];}}return Integer.parseInt(new String(new_code_bytes).trim());}catch(Exception e){e.printStackTrace();}return 0;}

最后对代码进行逐行遍历,遍历中为防止try catch块被遍历到,使用行号来限制遍历的范围。检测代码行是否包含通过Intent获取参数,及该行是否被try catch 包裹,如果上述两个条件均被触发,那么就作为一个问题进行上报。示例代码如下,其中get_code_line_index方法用于获取代码的行号,获取的方式是截取代码行的首字符的数值,以确定是否在代码包裹的范围内。

private void analyzeMethod(JavaClass javaClass, Method m, ClassContext classContext) throws CFGBuilderException {HashMap<String, List<Location>> all_line_location = (HashMap<String, List<Location>>) get_line_location(m, classContext);Code code = m.getCode();StringCodeAnalysis sca = new StringCodeAnalysis(code);String[] codes = sca.codes_String_Array();int code_length = sca.get_Code_Length(sca.get_First_Code(codes));int[] exception_scop = sca.getExceptionScope();for(int i=1; i<codes.length; i++){int line_index = sca.get_code_line_index(codes[i]);if (line_index < code_length){if(codes[i].toLowerCase().contains("invokevirtual") &&(codes[i].contains("android.content.Intent.get")  || codes[i].contains("android.os.Bundle.get"))){if(exception_scop.length == 0){......}else{boolean is_scope = false;for(int j=0; j<exception_scop.length; j+=2){int start = exception_scop[j];int end = exception_scop[j+1];if(line_index >= start && line_index <= end){is_scope = true;}if(is_scope){break;}}if(!is_scope){String method_name = get_method_name(codes[i]);if(all_line_location.containsKey(method_name)){for(Location loc : all_line_location.get(method_name)){bugReporter.reportBug(new BugInstance(this, LOCAL_DENIAL_SERVICE_TYPE, Priorities.NORMAL_PRIORITY).addClass(javaClass).addMethod(javaClass, m).addSourceLine(classContext, m, loc));}}else {bugReporter.reportBug(new BugInstance(this, LOCAL_DENIAL_SERVICE_TYPE, Priorities.NORMAL_PRIORITY).addClass(javaClass).addMethod(javaClass, m));}}}}}}}

上面详细叙述了如何构造自己的问题检测代码,完成检测方法的书写后,下一步就是在配置文件中对检测方法进行注册,才能使检测代码运转起来。

需要在两个文件中进行注册,第一个是findbugs.xml,注册示例如下:

<Detector class="com.h3xstream.findsecbugs.android.LocalDenialOfServiceDetector" reports="LOCAL_DENIAL_SERVICE"/>
<BugPattern type="LOCAL_DENIAL_SERVICE" abbrev="SECLDOS" category="Android安全问题" cweid="276"/>

其中Detector用于注册该检测方法的位置及其唯一标识,BugPattern用于对检测出的问题进行归类,方便展示,如此处归类到”Android安全问题”中,那么在生成报告的时候问题也将被归类到”Android安全问题”中。

第二个是messages.xml注册,注册示例如下,该注册主要是对该问题进行说明,包括问题的危害及修复方法。

<Detector class="com.h3xstream.findsecbugs.android.LocalDenialOfServiceDetector">
<Details>Local Denial of Service.</Details>
</Detector>
<BugPattern type="LOCAL_DENIAL_SERVICE">
<ShortDescription>本地拒绝服务</ShortDescription>
<LongDescription>通过Intent接收的参数未进行异常捕获,导致出现异常使得应用崩溃</LongDescription>
<Details>
<![CDATA[
    <p>
        <b>危害:</b><br/>
        <pre>
            应用崩溃无法使用,影响用户体验;
            被竞争对手利用,进行点对点攻击。
        </pre>
    </p>
    <p>
        <b>错误代码:</b><br/>
        <pre>
            bundle.getString(""); //未try/catch
            intent.getStringExtra(""); //未try/catch
        </pre>
    </p>
    <p>
        <b>解决方案:</b><br/>
        <pre>
            对通过Intent接收的参数处理时,进行严格的异常捕获。
            try {
                bundle.getString("");
                intent.getStringExtra(""); 
            }catch (Exception e){}
        </pre>
    </p>
]]>
</Details>
</BugPattern>
<BugCode abbrev="SECLDOS">本地拒绝服务</BugCode>

一切完成就绪后使用Maven进行打包,就生产了供FindBugs集成开发工具插件使用的JAR包,完成安装并重启,即可使用自定义插件对特定问题进行检测。 最终分析的效果图如下图所示:

分析效果图

本文介绍了Android集成开发环境Android Studio的代码实时检测工具Code Arbiter的产生原因及代码实现,最后展示了分析的效果。通过Code Arbiter在生产环境中的应用,其检测效果还是相当不错,能够发现很多编码过程中存在的问题。但是Code Arbiter仍然存在许多不足,需要优化。后续将在以下两个方面对工具进行改进: 1. 扩大漏洞检测范围,使Code Arbiter能够囊括Android编码常见安全问题; 2. 优化漏洞检测规则,提高检测的准确性,减少误报。

建弋,2016年加入美团,目前主要负责金融部门相关的安全工作。对于代码审计/漏洞扫描感兴趣的同学,可以阅读本人Freebuf上发表的相关文章,期待与大家共同学习共同提高。

美团金融服务平台-安全与合规中心致力于维护美团金融平台的安全工作,努力构建行业顶级安全架构,打造行业领先安全产品。欢迎各位行业同仁加入我们,共同建设美团金融安全。 联系邮箱:isec#meituan.com

【思考题】

文中描述了Android IDE漏洞扫描工具产生的前因后果,对实现的代码也进行了概括总结。不管是在IDE集成开发环境下的源码扫描,还是对APK文件的解包扫描,其最终的目的还是为了保证应用的安全性,那么大家在日常的工作中对于移动安全建设,以及互联网快速迭代环境下业务安全问题的排查有何独到的见地呢?希望不吝赐教。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/478480.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

领域应用 | OMAHA联盟发布“疾病临床表现”、“中毒”知识图谱及OMAHA知识库

转载公众号 | OMAHA联盟资源发布OMAHA已建立“七巧板”医学术语集、“汇知”医学知识图谱、元数据等资源&#xff0c;将于每月发布其中一项。具体各项发布时间为&#xff1a;“七巧板”术语集于每季度第一个月20日发布&#xff0c; “汇知”图谱于每季度第二个月20日发布&#…

聊聊推荐系统中的偏差

文 | 成指导源 | 知乎背景推荐系统中大量使用用户行为数据&#xff0c;作为系统学习的标签或者说信号。但用户行为数据天生存在各式各样的偏差&#xff08;bias&#xff09;&#xff0c;如果直接作为信号的话&#xff0c;学习出的模型参数不能准确表征用户在推荐系统中的真实行…

云端的SRE发展与实践

本文根据作者在美团点评第21期技术沙龙的分享记录整理而成。 SRE&#xff08;Site Reliability Engineering&#xff09;是Google于2003年提出的概念&#xff0c;将软件研发引入运维工作。现在渐渐已经成为各大互联网公司技术团队的标配。 美团点评作为综合性多业务的互联网生活…

一种单独适配于NER的数据增强方法:DAGA

链接&#xff1a;http://www.elecfans.com/d/1468784.html 本文首先介绍传统的数据增强在NER任务中的表现&#xff0c;然后介绍一种单独适配于NER的数据增强方法&#xff0c;这种方法生成的数据更具丰富性、数据质量更高。0 前言 在NLP中有哪些数据增强技术&#xff1f;这一定是…

LeetCode 80. 删除排序数组中的重复项 II

1. 题目 给定一个排序数组&#xff0c;你需要在原地删除重复出现的元素&#xff0c;使得每个元素最多出现两次&#xff0c;返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 来源&#xff1a;力扣…

技术沙龙 | 图神经网络(GNN)最新研究进展分享

由于深度学习在可推理和可解释性方面的局限性&#xff0c;结合图计算与深度学习的图神经网络 ( GNN ) 成为近期学术界和工业界研究的热点新方向之一&#xff0c;并在社交网络、推荐系统等领域得到了广泛的应用。本次技术沙龙&#xff0c;由北京邮电大学 GAMMA Lab 博士生纪厚业…

科研福利!国内TOP3的超算中心,免费领2000核时计算资源

长久以来&#xff0c;超级计算机一直是各国竞相角逐的科技制高点&#xff0c;也是国家综合科技实力的体现&#xff0c;尤其是近几年&#xff0c;中国和美国在超算领域的竞争已经进入“白热化”。2020年&#xff0c;我国超级计算机在《全球超级计算机500强榜单》中首次超越美国&…

深度学习在美团推荐平台排序中的运用

美团作为国内最大的生活服务平台&#xff0c;业务种类涉及食、住、行、玩、乐等领域&#xff0c;致力于让大家吃得更好&#xff0c;活得更好&#xff0c;有数亿用户以及丰富的用户行为。随着业务的飞速发展&#xff0c;美团的用户和商户数在快速增长。在这样的背景下&#xff0…

LeetCode 451. 根据字符出现频率排序(map+优先队列)

1. 题目 给定一个字符串&#xff0c;请将字符串里的字符按照出现的频率降序排列。 输入: "tree"输出: "eert"2. 优先队列解题 先用map统计字符出现次数再将字符何其次数插入优先队列出队 struct cmp { //写在类内也可以&#xff0c;写在函数里也行bool…

论文浅尝 - AAAI2020 | 小样本知识图谱补全

笔记整理 | 刘克欣&#xff0c;天津大学硕士链接&#xff1a;https://arxiv.org/pdf/1911.11298.pdf动机知识图谱对于许多下游应用&#xff08;例如搜索&#xff0c;知识问答和语义网&#xff09;至关重要。然而&#xff0c;现有知识图谱面临不完整的问题。知识图谱补全工作能让…

ACL 2021|美团提出基于对比学习的文本表示模型,效果提升8%

文 | 渊蒙 如寐 思睿等尽管基于BERT的模型在NLP诸多下游任务中取得了成功&#xff0c;直接从BERT导出的句向量表示往往被约束在一个很小的区域内&#xff0c;表现出很高的相似度&#xff0c;因而难以直接用于文本语义匹配。为解决BERT原生句子表示这种“坍缩”现象&#xff0c;…

Android远程调试的探索与实现

作为移动开发者&#xff0c;最头疼的莫过于遇到产品上线以后出现了Bug&#xff0c;但是本地开发环境又无法复现的情况。常见的调查线上棘手问题方式大概如下&#xff1a; 方法优点缺点联系用户安装已添加测试日志的APK方便定位问题需要用户积极配合&#xff0c;如果日志添加不全…

NLP实战-中文命名实体识别

NLP实战-中文命名实体识别&#xff1a;https://zhuanlan.zhihu.com/p/61227299

超硬核 ICML’21 | 如何使自然语言生成提速五倍,且显存占用减低99%

文 | 炼丹学徒编 | 小轶我们忽略掉引言和介绍&#xff0c;直接把工作的效果丢上来&#xff0c;相信就足够令自然语言生成的相关同学心动——对于任何一个已有的Transformer生成模型&#xff0c;只需根据本文算法更改attention的计算顺序&#xff0c;就可以实现成倍速度提升&…

论文浅尝 | Convolutional 2D knowledge graph embedding

笔记整理 | 孙悦&#xff0c;天津大学1. 介绍&#xff1a;知识图的链接预测是预测实体之间缺失关系的任务。先前有关链接预测的工作集中在浅&#xff0c;快速的模型上&#xff0c;这些模型可以缩放到大型知识图例如基于基于平移变换的 TransE 系列。但是&#xff0c;这些模型比…

sysbench在美团点评中的应用

如何快速入门数据库&#xff1f;以我个人经验来看&#xff0c;数据库功能和性能测试是一条不错的捷径。当然从公司层面&#xff0c;数据库测试还有更多实用的功能。这方面&#xff0c;美团点评使用的是知名工具sysbench&#xff0c;主要是用来解决以下几个问题&#xff1a; 统一…

常见中文NER数据集大盘点

常见中文NER数据集大盘点 https://zhuanlan.zhihu.com/p/164966421

[中文事件抽取]DCFEE: A Document-level Chinese Financial Event Extraction System based on Automatically Lab

[中文事件抽取]DCFEE: A Document-level Chinese Financial Event Extraction System based on Automatically Lab&#xff1a; ACL 2018DCFEE: A Document-level Chinese Financial Event Extraction System based on Automatically Labeled Training DataAuthorHang Yang, Yu…

论文浅尝 - ACL2020 | 通过集成知识转换进行多语言知识图谱补全

笔记整理 | 谭亦鸣&#xff0c;东南大学博士生概述预测图谱中缺失的事实(fact)是知识图谱构建与推理中的一个重要任务&#xff0c;近年来也被许多KG embedding研究的关注对象。虽然目前的KG embedding方法主要学习和预测的是单个图谱中的事实&#xff0c;但是考虑到KG之间不同规…