zoukankan
html css js c++ java
JS代码混淆初步
一直以来,JS前端代码因为必须经过IE明文解析,某些加密的JS如:JScript.Encode也因为树大招风,早就被人破解了。还有些加密的手段,通过复杂的变换,改变源码,但最终都逃不脱最后的审判,像unescape,document.write,eval语句来还原。对于JS代码的保护,最好的手段就是混淆,混淆的目的就是让读懂代码的成本比直接写代码的成本高,混淆不是不可破解的,只是增加破解成本,JAVA,C#的加密都是采用混淆。这样对于非常核心的代码,混淆起不到保护代码的作用,不过JS有这样的代码么?本程序没有用到编译原理,其分词解析思想是基于mechiland(
http://www.jzchen.net
)的代码高亮程序,参考了蓝色经典的
加密混淆专题讨论
。
<
HTML
><
HEAD
><
TITLE
>
Cunfusion
</
TITLE
>
<
META
content
="MSHTML 6.00.2800.1528"
name
=GENERATOR
>
<
META
content
=""
name
=Author
>
<
META
content
=""
name
=Keywords
>
<
META
content
=""
name
=Description
></
HEAD
>
<
BODY
>
<
SCRIPT
language
=JavaScript
>
<!--
/**/
/*
*
** ==================================================================================================
** 类名:CLASS_CONFUSION
** 功能:JS混淆
** 示例:
---------------------------------------------------------------------------------------------------
var xx = new CLASS_CONFUSION(code);
document.getElementById("display").innerHTML = xx.confusion();
---------------------------------------------------------------------------------------------------
** 作者:ttyp
** 邮件:ttyp@21cn.com
** 日期:2006-3-20
** 版本:0.12
** ==================================================================================================
*
*/
function
CLASS_CONFUSION(code)
{
//
哈希表类
function
Hashtable()
{
this
._hash
=
new
Object();
this
.add
=
function
(key,value)
{
if
(
typeof
(key)
!=
"
undefined
"
)
{
if
(
this
.contains(key)
==
false
)
{
this
._hash[key]
=
typeof
(value)
==
"
undefined
"
?
null
:value;
return
true
;
}
else
{
return
false
;
}
}
else
{
return
false
;
}
}
this
.remove
=
function
(key)
{
delete
this
._hash[key];}
this
.count
=
function
()
{
var
i
=
0
;
for
(
var
k
in
this
._hash)
{i
++
;}
return
i;}
this
.items
=
function
(key)
{
return
this
._hash[key];}
this
.contains
=
function
(key)
{
return
typeof
(
this
._hash[key])
!=
"
undefined
"
;}
this
.clear
=
function
()
{
for
(
var
k
in
this
._hash)
{
delete
this
._hash[k];}
}
}
function
VariableMap(parent)
{
this
.table
=
new
Hashtable();
this
.level
=
parent
?
parent.level
+
1
:
0
;
this
.parent
=
parent;
this
.add
=
function
(key,value)
{
this
.table.add(key,value)}
;
this
.items
=
function
(key)
{
return
this
.table.items(key)}
;
this
.count
=
function
()
{
return
this
.table.count()}
;
this
.contains
=
function
(key)
{
return
this
.table.contains(key);}
this
.isParameter
=
false
;
}
this
._caseSensitive
=
true
;
//
字符串转换为哈希表
this
.str2hashtable
=
function
(key,cs)
{
var
_key
=
key.split(
/
,
/
g);
var
_hash
=
new
Hashtable();
var
_cs
=
true
;
if
(
typeof
(cs)
==
"
undefined
"
||
cs
==
null
)
{
_cs
=
this
._caseSensitive;
}
else
{
_cs
=
cs;
}
for
(
var
i
in
_key)
{
if
(_cs)
{
_hash.add(_key[i]);
}
else
{
_hash.add((_key[i]
+
""
).toLowerCase());
}
}
return
_hash;
}
//
获得需要转换的代码
this
._codetxt
=
code;
if
(
typeof
(syntax)
==
"
undefined
"
)
{
syntax
=
""
;
}
this
._deleteComment
=
false
;
//
是否大小写敏感
this
._caseSensitive
=
true
;
//
得到关键字哈希表
this
._keywords
=
this
.str2hashtable(
"
switch,case,delete,default,typeof,for,in,function,void,this,boolean,while,if,return,new,true,false,try,catch,throw,null,else,do,var
"
);
this
._function
=
this
.str2hashtable(
"
function
"
);
this
._var
=
"
var
"
;
this
._beginBlock
=
"
{
"
;
this
._endBlock
=
"
}
"
;
this
._window
=
this
.str2hashtable(
"
alert,escape,unescape,document,parseInt,parseFloat
"
);
//
得到内建对象哈希表
this
._commonObjects
=
this
.str2hashtable(
"
String,Number,Boolean,RegExp,Error,Math,Date,Object,Array,Global
"
);
//
得到分割字符
this
._wordDelimiters
=
"
,.?!;:\\/<>(){}[]\
"
'\r\n\t
=+-|*%
@#$
^&
"
;
//引用字符
this._quotation = this.str2hashtable(
"
\
"
,'
"
);
//
行注释字符
this
._lineComment
=
"
//
"
;
//
转义字符
this
._escape
=
"
\\
"
;
//
多行引用开始
this
._commentOn
=
"
/*
"
;
//
多行引用结束
this
._commentOff
=
"
*/
"
;
this
._execute
=
"
eval
"
;
//
引用调用字符
this
._call
=
"
.
"
;
this
._varPause
=
"
=
"
;
this
._varContinue
=
"
,
"
;
//
变量个数
this
._varNum
=
0
;
this
.confusion
=
function
()
{
var
codeArr
=
new
Array();
var
word_index
=
0
;
var
htmlTxt
=
new
Array();
//
得到分割字符数组(分词)
for
(
var
i
=
0
; i
<
this
._codetxt.length; i
++
)
{
if
(
this
._wordDelimiters.indexOf(
this
._codetxt.charAt(i))
==
-
1
)
{
//
找不到关键字
if
(codeArr[word_index]
==
null
||
typeof
(codeArr[word_index])
==
'undefined')
{
codeArr[word_index]
=
""
;
}
codeArr[word_index]
+=
this
._codetxt.charAt(i);
}
else
{
if
(
typeof
(codeArr[word_index])
!=
'undefined'
&&
codeArr[word_index].length
>
0
)
word_index
++
;
codeArr[word_index
++
]
=
this
._codetxt.charAt(i);
}
}
var
quote_opened
=
false
;
//
引用标记
var
slash_star_comment_opened
=
false
;
//
多行注释标记
var
slash_slash_comment_opened
=
false
;
//
单行注释标记
var
line_num
=
1
;
//
行号
var
quote_char
=
""
;
//
引用标记类型
var
call_opened
=
false
;
var
call_string
=
""
;
var
var_opened
=
false
;
var
var_pause
=
false
;
var
function_opened
=
false
;
var
parameter_opened
=
false
;
var
var_map
=
new
VariableMap();
var
cur_var_map
=
var_map;
var
execute_opened
=
false
;
//
按分割字,分块显示
for
(
var
i
=
0
; i
<=
word_index; i
++
)
{
//
单独处理指针引用
if
(call_opened
&&
typeof
(codeArr[i])
!=
"
undefined
"
)
{
if
(call_string.length
==
0
)
{
if
(
this
.isVar(codeArr[i]))
{
call_string
+=
codeArr[i];
}
else
{
htmlTxt[htmlTxt.length]
=
"
[\
""
+ this.toHex(call_string) +
"
\
"
]
"
;
if
(codeArr[i]
!=
this
._call)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
call_opened
=
false
;
}
call_string
=
""
;
}
}
else
{
if
(
!
this
.isVar(codeArr[i]))
{
htmlTxt[htmlTxt.length]
=
"
[\
""
+ this.toHex(call_string) +
"
\
"
]
"
;
if
(codeArr[i]
!=
this
._call)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
call_opened
=
false
;
}
call_string
=
""
;
}
else
{
htmlTxt[htmlTxt.length]
=
"
[\
""
+ this.toHex(call_string) +
"
\
"
]
"
;
}
}
continue
;
}
//
处理空行(由于转义带来)
if
(
typeof
(codeArr[i])
==
"
undefined
"
||
codeArr[i].length
==
0
)
{
continue
;
}
else
if
(codeArr[i]
==
"
"
)
{
htmlTxt[htmlTxt.length]
=
"
"
;
}
else
if
(codeArr[i]
==
"
\n
"
)
{
//
处理换行
}
else
if
(codeArr[i]
==
"
\r
"
)
{
slash_slash_comment_opened
=
false
;
quote_opened
=
false
;
var_opened
=
false
;
htmlTxt[htmlTxt.length]
=
"
\r\n
"
;
line_num
++
;
//
处理function里的参数标记
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
this
.isFunction(codeArr[i]))
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
function_opened
=
true
;
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
"
(
"
)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
if
(function_opened
==
true
)
{
function_opened
=
false
;
var_opened
=
true
;
cur_var_map
=
new
VariableMap(cur_var_map);
cur_var_map.isParameter
=
true
;
}
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
"
)
"
)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
//
处理var a = new Class(),b=new Date();类似的问题
if
(cur_var_map.isParameter)
{
var_opened
=
false
;
var_pause
=
false
;
}
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
"
;
"
)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
var_opened
=
false
;
var_pause
=
false
;
if
(execute_opened)
{
execute_opened
=
false
;
}
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
this
._var)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
var_opened
=
true
;
var_pause
=
false
;
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
this
._varPause)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
var_pause
=
true
;
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
this
._varContinue)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
var_pause
=
false
;
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
this
._beginBlock)
{
cur_var_map
=
new
VariableMap(cur_var_map);
var_opened
=
false
;
htmlTxt[htmlTxt.length]
=
codeArr[i];
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
this
._endBlock)
{
cur_var_map
=
cur_var_map.parent;
if
(cur_var_map.isParameter)
{
cur_var_map
=
cur_var_map.parent;
}
htmlTxt[htmlTxt.length]
=
codeArr[i];
//
处理引用调用
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
this
._call)
{
//
判断引用(.)后面第一个是否为字母货_$
if
(i
<
word_index
-
1
)
{
if
(
this
.isVar(codeArr[i
+
1
]))
{
if
(call_opened)
{
htmlTxt[htmlTxt.length]
=
this
.toHex(call_string);
}
call_opened
=
true
;
}
else
{
htmlTxt[htmlTxt.length]
=
this
._call;
}
}
else
{
htmlTxt[htmlTxt.length]
=
this
._call;
}
//
处理关键字
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
this
.isKeyword(codeArr[i]))
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
//
处理eval后的字符串
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
codeArr[i]
==
this
._execute)
{
htmlTxt[htmlTxt.length]
=
"
window[\
""
+ this.toHex(codeArr[i]) +
"
\
"
]
"
;
execute_opened
=
true
;
//
window内置对象
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
this
.isWindow(codeArr[i]))
{
htmlTxt[htmlTxt.length]
=
"
window[\
""
+ this.toHex(codeArr[i]) +
"
\
"
]
"
;
//
处理普通对象
}
else
if
(
!
slash_slash_comment_opened
&&!
slash_star_comment_opened
&&
!
quote_opened
&&
this
.isCommonObject(codeArr[i]))
{
htmlTxt[htmlTxt.length]
=
"
window[\
""
+ this.toHex(codeArr[i]) +
"
\
"
]
"
;
//
处理双引号(引号前不能为转义字符)
}
else
if
(
!
slash_star_comment_opened
&&!
slash_slash_comment_opened
&&
this
._quotation.contains(codeArr[i]))
{
if
(quote_opened)
{
//
是相应的引号
if
(quote_char
==
codeArr[i])
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
quote_opened
=
false
;
quote_char
=
""
;
}
else
{
htmlTxt[htmlTxt.length]
=
this
.toHex(codeArr[i]);
}
}
else
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
quote_opened
=
true
;
quote_char
=
codeArr[i];
}
//
处理转义字符
}
else
if
(codeArr[i]
==
this
._escape)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
if
(i
<
word_index
-
1
)
{
if
(codeArr[i
+
1
].charCodeAt(
0
)
>=
32
&&
codeArr[i
+
1
].charCodeAt(
0
)
<=
127
)
{
htmlTxt[htmlTxt.length]
=
codeArr[i
+
1
].substr(
0
,
1
);
htmlTxt[htmlTxt.length]
=
this
.toHex(codeArr[i
+
1
].substr(
1
));
i
=
i
+
1
;
}
}
//
处理多行注释的开始
}
else
if
(
!
slash_slash_comment_opened
&&
!
slash_star_comment_opened
&&!
quote_opened
&&
this
.isStartWith(
this
._commentOn,codeArr,i))
{
slash_star_comment_opened
=
true
;
htmlTxt[htmlTxt.length]
=
this
._commentOn;
i
=
i
+
this
.getSkipLength(
this
._commentOn);
//
处理单行注释
}
else
if
(
!
slash_slash_comment_opened
&&
!
slash_star_comment_opened
&&!
quote_opened
&&
this
.isStartWith(
this
._lineComment,codeArr,i))
{
slash_slash_comment_opened
=
true
;
if
(
!
this
._deleteComment)
{
htmlTxt[htmlTxt.length]
=
this
._lineComment;
}
i
=
i
+
this
.getSkipLength(
this
._lineComment);
//
处理忽略词
}
else
if
(
!
slash_slash_comment_opened
&&
!
slash_star_comment_opened
&&!
quote_opened
&&
this
.isStartWith(
this
._ignore,codeArr,i))
{
slash_slash_comment_opened
=
true
;
htmlTxt[htmlTxt.length]
=
this
._ignore;
i
=
i
+
this
.getSkipLength(
this
._ignore);
//
处理多行注释结束
}
else
if
(
!
quote_opened
&&!
slash_slash_comment_opened
&&
this
.isStartWith(
this
._commentOff,codeArr,i))
{
if
(slash_star_comment_opened)
{
slash_star_comment_opened
=
false
;
if
(
!
this
._deleteComment)
{
htmlTxt[htmlTxt.length]
=
this
._commentOff;
}
i
=
i
+
this
.getSkipLength(
this
._commentOff);
}
}
else
{
//
不是在字符串中
if
(
!
quote_opened)
{
//
如果不是在注释重
if
(
!
slash_slash_comment_opened
&&
!
slash_star_comment_opened)
{
//
不是在定义变量时
if
(
!
var_opened)
{
if
(
this
.translateVar(cur_var_map,codeArr[i])
==
""
)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
}
else
{
htmlTxt[htmlTxt.length]
=
this
.translateVar(cur_var_map,codeArr[i]);
}
}
else
{
//
不是在暂停变量定义时
if
(var_pause)
{
if
(
this
.translateVar(cur_var_map,codeArr[i])
==
""
)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
}
else
{
htmlTxt[htmlTxt.length]
=
this
.translateVar(cur_var_map,codeArr[i]);
}
}
else
{
//
变量符合命名规则,并且(变量前为空格或制表符或逗号如:var a;var a;var a,b;,还有如果是函数参数,如:function(a,b,c)
if
(
this
.isVar(codeArr[i])
&&
(i
>
0
&&
codeArr[i
-
1
]
==
"
"
||
codeArr[i
-
1
]
==
"
\t
"
||
codeArr[i
-
1
]
==
this
._varContinue
||
cur_var_map.isParameter))
{
var
name
=
this
.getRandName();
cur_var_map.add(codeArr[i],name);
htmlTxt[htmlTxt.length]
=
this
.translateVar(cur_var_map,codeArr[i]);
}
else
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
}
}
}
//
注释中
}
else
{
if
(
!
this
._deleteComment)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
}
}
}
else
{
if
(execute_opened)
{
if
(
this
.translateVar(cur_var_map,codeArr[i])
==
""
)
{
htmlTxt[htmlTxt.length]
=
codeArr[i];
}
else
{
htmlTxt[htmlTxt.length]
=
this
.translateVar(cur_var_map,codeArr[i]);
}
}
else
{
htmlTxt[htmlTxt.length]
=
this
.toHex(codeArr[i]);
}
}
}
}
return
htmlTxt.join(
""
);
}
this
.isStartWith
=
function
(str,code,index)
{
if
(
typeof
(str)
!=
"
undefined
"
&&
str.length
>
0
)
{
var
cc
=
new
Array();
for
(
var
i
=
index;i
<
index
+
str.length;i
++
)
{
cc[cc.length]
=
code[i];
}
var
c
=
cc.join(
""
);
if
(
this
._caseSensitive)
{
if
(str.length
>=
code[index].length
&&
c.indexOf(str)
==
0
)
{
return
true
;
}
}
else
{
if
(str.length
>=
code[index].length
&&
c.toLowerCase().indexOf(str.toLowerCase())
==
0
)
{
return
true
;
}
}
return
false
;
}
else
{
return
false
;
}
}
this
.isFunction
=
function
(val)
{
return
this
._function.contains(
this
._caseSensitive
?
val:val.toLowerCase());
}
this
.isKeyword
=
function
(val)
{
return
this
._keywords.contains(
this
._caseSensitive
?
val:val.toLowerCase());
}
this
.isWindow
=
function
(val)
{
return
this
._window.contains(
this
._caseSensitive
?
val:val.toLowerCase());
}
this
.isCommonObject
=
function
(val)
{
return
this
._commonObjects.contains(
this
._caseSensitive
?
val:val.toLowerCase());
}
this
.getSkipLength
=
function
(val)
{
var
count
=
0
;
for
(
var
i
=
0
;i
<
val.length;i
++
)
{
if
(
this
._wordDelimiters.indexOf(val.charAt(i))
>=
0
)
{
count
++
;
}
}
if
(count
>
0
)
{
count
=
count
-
1
;
}
return
count;
}
//
字符串转换为16进制形式
this
.toHex
=
function
(val)
{
var
str
=
new
Array();
for
(
var
i
=
0
;i
<
val.length;i
++
)
{
var
c
=
val.charCodeAt(i);
if
(c
>=
0
&&
c
<
256
)
{
str[str.length]
=
"
\\x
"
+
val.charCodeAt(i).toString(
16
);
}
else
{
str[str.length]
=
"
\\u
"
+
val.charCodeAt(i).toString(
16
);
}
}
return
str.join(
""
);
}
//
获得变量随机名
this
.getRandName
=
function
()
{
var
style
=
parseInt(Math.random()
*
4
);
var
len
=
parseInt(Math.random()
*
9
)
+
1
;
var
n
=
[];
this
._varNum
++
;
var
c
=
"
abcdefghijklmnopqrstuvwsyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$
"
;
for
(
var
i
=
0
;i
<
len;i
++
)
{
n[n.length]
=
c.charAt(parseInt(Math.random()
*
54
));
}
return
n.join(
""
)
+
this
._varNum;
}
//
是否符合变量命名字首规则
this
.isVar
=
function
(val)
{
return
/^
[a
-
zA
-
Z_\$].
*
$
/
.test(val);
}
//
翻译变量,如果返回为空则不存在此变量
this
.translateVar
=
function
(node,key)
{
if
(node.contains(key))
{
return
node.items(key);
}
var
cn
=
node.parent;
while
(cn
!=
null
)
{
if
(cn.contains(key))
{
return
cn.items(key);
}
cn
=
cn.parent;
}
return
""
;
}
}
function
doConfusion(o)
{
var
htmltxt
=
""
;
if
(o
==
null
)
{
alert(
"
domNode is null!
"
);
return
;
}
var
_codetxt
=
""
;
if
(
typeof
(o)
==
"
object
"
)
{
switch
(o.tagName)
{
case
"
TEXTAREA
"
:
case
"
INPUT
"
:
_codetxt
=
o.value;
break
;
case
"
DIV
"
:
case
"
SPAN
"
:
_codetxt
=
o.innerText;
break
;
default
:
_codetxt
=
o.innerHTML;
break
;
}
}
else
{
_codetxt
=
o;
}
var
_syn
=
new
CLASS_CONFUSION(_codetxt);
htmltxt
=
_syn.confusion();
return
htmltxt;
}
function
go()
{
var
code
=
document.getElementById(
"
code
"
).value;
var
xx
=
new
CLASS_CONFUSION(code);
var
a
=
new
Date();
document.getElementById(
"
display
"
).value
=
xx.confusion();
alert(
"
共花:
"
+
(
new
Date().getTime()
-
a.getTime())
+
"
ms
"
);
}
//
-->
</
SCRIPT
>
<
TEXTAREA
id
=code
rows
=12
cols
=100
>
/*
这个是一个类
*/
function xx(num,str)
{
//说明
var a = num;
this.aa = a;
this.bb = function(){alert(str);}
this.cc = function(){for(var i=0;i
<
10
;i++){document.title
=i;}}
}
xx.prototype.dd
= function(){
alert("dd");
}
var a
= new
xx(100,"hello"),b
=new
xx(0,"ttyp");
eval("a.aa
=20");
a.bb();
b.dd();
alert(a.aa);
</TEXTAREA
>
<
BR
><
INPUT
onclick
=go()
type
=button
value
=go
><
br
>
<
textarea
id
=display
rows
=12
cols
=100
>
</
textarea
>
</
BODY
></
HTML
>
最后,本人原则上不赞成代码混淆加密,写本程序是出于研究目的,本程序本人原创,后续的代码压缩等功能陆续加上,程序中有任何问题请指正,任何转载请写明出处。
查看全文
相关阅读:
理解Java虚拟机——Java内存模型管理
Java 使用fastjson 将 json字符串写到文件中去
java 如何调用 linux or mac 命令行
MacOS 编译 openjdk8 并导入 Clion 调试
linux ls 命令超级详解
小 Q 与树 (点分治)
mysql 索引策略
java中serialVersionUID作用
通过源码分析Spring Security用户认证流程
使用PowerMockRunner和Mockito编写单元测试用例详解
原文地址:https://www.cnblogs.com/top5/p/2099748.html
最新文章
jmeter场景设计
安全测试面试
解决levelDB导致ActiveMQ一直占用1个CPU的100%使用率的问题
练习1
html-css
Monkey
ADB 工具
文章标题目录锚点功能
签到抽奖功能——js常见前端抽奖需求
CodeForces 146E Lucky Subsequence (排列组合 + DP)
热门文章
CodeForces 1467D Sum of Paths (动态规划)
Java 桥接方法
nginx 替换
JVM方法调用原理
JVM 编译的细节
Base32 编码与解码
spring 多数据源的使用
横向拆分与纵向拆分
深入理解Java虚拟机——Java对象是否存活
深入理解Java虚拟机——Hotpot虚拟机对象
Copyright © 2011-2022 走看看