怎么搭建服务器代码

源文件名:HttpsServer.java,用Java写的一个简单的HTTP服务器,代码实现了定义服务器端口,建立SSL Socket连接,演示如何为每一个连接建立线程以及如何获取客户端请求并发送回应数据,若请求或解析过程中出现错误,则返回400错误HTTP头信息,实现了基本的HTTP服务器功能。
import java.io.*;
import java.net.*;
import javax.net.*;
import javax.net.ssl.*;
import java.security.*;
import java.util.*;
public class HttpsServer
// keystore文件名
String keystore = &serverkeys&;
// keystore密码
char keystorepass[] = &hellothere&.toCharArray();
// key密码
char keypassword[] = &hiagain&.toCharArray();
// 使用缺省的433端口
public static final int HTTPS_PORT = 443;
public ServerSocket getServer() throws Exception
KeyStore ks = KeyStore.getInstance(&JKS&);
ks.load(new FileInputStream(keystore), keystorepass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(&SunX509&);
kmf.init(ks, keypassword);
SSLContext sslcontext = SSLContext.getInstance(&SSLv3&);
sslcontext.init(kmf.getKeyManagers(), null, null);
ServerSocketFactory ssf = sslcontext.getServerSocketFactory();
//建立SSL Socket连接
SSLServerSocket serversocket =(SSLServerSocket) ssf.createServerSocket(HTTPS_PORT);
public void run()
//为每一个连接建立线程
listen = getServer();
while (true)
Socket client = listen.accept();
ProcessConnection cc = new ProcessConnection(client);
catch (Exception e)
System.out.println(&Exception: & + e.getMessage());
public static void main(String argv[]) throws Exception
HttpsServer https = new HttpsServer();
https.run();
class ProcessConnection extends Thread
DataOutputS
public ProcessConnection(Socket s)
//定义客户端请求流
is =new BufferedReader(new InputStreamReader(client.getInputStream()));
//定义服务器响应流
os = new DataOutputStream(client.getOutputStream());
catch (IOException e)
System.out.println(&Exception: & + e.getMessage());
this.start();
public void run()
// 接受客户端请求并解析请求
String request = is.readLine();
System.out.println(&Request: & + request);
StringTokenizer st = new StringTokenizer(request);
if ((st.countTokens() &= 2) &&
st.nextToken().equals(&GET&))
if ((request = st.nextToken()).startsWith(&/&))
request = request.substring(1);
if (request.equals(&&))
request = request + &index.html&;
File f = new File(request);
shipDocument(os, f);
os.writeBytes(&400 Bad Request&);
client.close();
catch (Exception e)
System.out.println(&Exception: & + e.getMessage());
public static void shipDocument(DataOutputStream out, File f)
throws Exception
DataInputStream in =
new DataInputStream (new FileInputStream(f));
int len = (int) f.length();
byte[] buf = new byte[len];
in.readFully(buf);
in.close();
out.writeBytes(&HTTP/1.0 200 OK\r\n&);
out.writeBytes(&Content-Length: & + f.length() + &\r\n&);
out.writeBytes(&Content-Type:text/html\r\n\r\n&);
out.write(buf);
out.flush();
catch (Exception e)
// 返回出错信息
out.writeBytes(&&html&&head&&title&error&/title&&+&&/head&&body&\r\n\r\n&);
out.writeBytes(&HTTP/1.0 400 & + e.getMessage() + &\r\n&);
out.writeBytes(&Content-Type: text/html\r\n\r\n&);
out.writeBytes(&&/body&&/html&&);
out.flush();
out.close();
本类推荐文章
本类最新更新
最新源码下载用一个printf()调用实现一个web服务器 - 文章 - 伯乐在线
& 用一个printf()调用实现一个web服务器
一个小伙伴转发了一个可能我们都知道的的。每次我读到这个列表的时候,这一部分就会跳出来:
Jeff Dean有次用一句实现了一个服务器,而其他工程师添加了数千行注释但是仍然不能完全弄清楚它是如何工作的。而这个程序正是如今的首页。
使用一句调用来实现一个服务器是很有可能的,但是我还没发现其他人做到。所以这次我读到这个列表时,我决定实现它。这里是它的代码,一个纯粹单一的调用,没有任何附加的变量或者宏(不用担心,我将会解释这段代码是如何工作的)。
#include &stdio.h&
int main(int argc, char *argv[])
printf("%*c%hn%*c%hn"
"\xeb\x3d\x48\x54\x54\x50\x2f\x31\x2e\x30\x20\x32"
"\x30\x30\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d"
"\x74\x79\x70\x65\x3a\x74\x65\x78\x74\x2f\x68\x74"
"\x6d\x6c\x0d\x0a\x0d\x0a\x3c\x68\x31\x3e\x48\x65"
"\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x3c\x2f"
"\x68\x31\x3e\x4c\x8d\x2d\xbc\xff\xff\xff\x48\x89"
"\xe3\x48\x83\xeb\x10\x48\x31\xc0\x50\x66\xb8\x1f"
"\x90\xc1\xe0\x10\xb0\x02\x50\x31\xd2\x31\xf6\xff"
"\xc6\x89\xf7\xff\xc7\x31\xc0\xb0\x29\x0f\x05\x49"
"\x89\xc2\x31\xd2\xb2\x10\x48\x89\xde\x89\xc7\x31"
"\xc0\xb0\x31\x0f\x05\x31\xc0\xb0\x05\x89\xc6\x4c"
"\x89\xd0\x89\xc7\x31\xc0\xb0\x32\x0f\x05\x31\xd2"
"\x31\xf6\x4c\x89\xd0\x89\xc7\x31\xc0\xb0\x2b\x0f"
"\x05\x49\x89\xc4\x48\x31\xd2\xb2\x3d\x4c\x89\xee"
"\x4c\x89\xe7\x31\xc0\xff\xc0\x0f\x05\x31\xf6\xff"
"\xc6\xff\xc6\x4c\x89\xe7\x31\xc0\xb0\x30\x0f\x05"
"\x4c\x89\xe7\x31\xc0\xb0\x03\x0f\x05\xeb\xc3",
((((unsigned long int)0x4005c8 + 12) && 16) & 0xffff),
0, 0x07D8 + 2,
(((unsigned long int)0x4005c8 + 12) & 0xffff)-
((((unsigned long int)0x4005c8 + 12) && 16) & 0xffff),
0, 0x07D8 );
12345678910111213141516171819202122232425262728
&&&&#include &stdio.h&&&&&&int main(int argc, char *argv[])&&&&{&&&& printf("%*c%hn%*c%hn"&&&&&&"\xeb\x3d\x48\x54\x54\x50\x2f\x31\x2e\x30\x20\x32"&&&& "\x30\x30\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d"&&&&&&"\x74\x79\x70\x65\x3a\x74\x65\x78\x74\x2f\x68\x74"&&&& "\x6d\x6c\x0d\x0a\x0d\x0a\x3c\x68\x31\x3e\x48\x65"&&&&&&"\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x3c\x2f"&&&&&&"\x68\x31\x3e\x4c\x8d\x2d\xbc\xff\xff\xff\x48\x89"&&&& "\xe3\x48\x83\xeb\x10\x48\x31\xc0\x50\x66\xb8\x1f"&&&&&&"\x90\xc1\xe0\x10\xb0\x02\x50\x31\xd2\x31\xf6\xff"&&&& "\xc6\x89\xf7\xff\xc7\x31\xc0\xb0\x29\x0f\x05\x49"&&&& "\x89\xc2\x31\xd2\xb2\x10\x48\x89\xde\x89\xc7\x31"&&&&&&"\xc0\xb0\x31\x0f\x05\x31\xc0\xb0\x05\x89\xc6\x4c"&&&& "\x89\xd0\x89\xc7\x31\xc0\xb0\x32\x0f\x05\x31\xd2"&&&&&&"\x31\xf6\x4c\x89\xd0\x89\xc7\x31\xc0\xb0\x2b\x0f"&&&& "\x05\x49\x89\xc4\x48\x31\xd2\xb2\x3d\x4c\x89\xee"&&&& "\x4c\x89\xe7\x31\xc0\xff\xc0\x0f\x05\x31\xf6\xff"&&&& "\xc6\xff\xc6\x4c\x89\xe7\x31\xc0\xb0\x30\x0f\x05"&&&&&&"\x4c\x89\xe7\x31\xc0\xb0\x03\x0f\x05\xeb\xc3",&&&& ((((unsigned long int)0x4005c8 + 12) && 16) & 0xffff), &&&& 0, 0x07D8 + 2, &&&&&&(((unsigned long int)0x4005c8 + 12) & 0xffff)-&&&&&&((((unsigned long int)0x4005c8 + 12) && 16) & 0xffff), &&&&&&0, 0x07D8 );&&&&}
这段代码只能在独有位编译器(版本是())的系统上运行,编译命令如下:
gcc -g web1.c -O webserver
gcc -g web1.c -O webserver
可能有些人会这样猜测:我用一个来作弊。这段代码可能不能在你的机器上运行,因为我对两个地址使用了硬编码。
下面这个版本是更加用户友好化的(更容易改变),但是你仍旧要改变两个值:和,稍后我会解释:
#include &stdio.h&
#include &stdlib.h&
#include &stdint.h&
#define FUNCTION_ADDR ((uint64_t)0x4005c8 + 12)
#define DESTADDR 0x07D8
#define a (FUNCTION_ADDR & 0xffff)
#define b ((FUNCTION_ADDR && 16) & 0xffff)
int main(int argc, char *argv[])
printf("%*c%hn%*c%hn"
"\xeb\x3d\x48\x54\x54\x50\x2f\x31\x2e\x30\x20\x32"
"\x30\x30\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d"
"\x74\x79\x70\x65\x3a\x74\x65\x78\x74\x2f\x68\x74"
"\x6d\x6c\x0d\x0a\x0d\x0a\x3c\x68\x31\x3e\x48\x65"
"\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x3c\x2f"
"\x68\x31\x3e\x4c\x8d\x2d\xbc\xff\xff\xff\x48\x89"
"\xe3\x48\x83\xeb\x10\x48\x31\xc0\x50\x66\xb8\x1f"
"\x90\xc1\xe0\x10\xb0\x02\x50\x31\xd2\x31\xf6\xff"
"\xc6\x89\xf7\xff\xc7\x31\xc0\xb0\x29\x0f\x05\x49"
"\x89\xc2\x31\xd2\xb2\x10\x48\x89\xde\x89\xc7\x31"
"\xc0\xb0\x31\x0f\x05\x31\xc0\xb0\x05\x89\xc6\x4c"
"\x89\xd0\x89\xc7\x31\xc0\xb0\x32\x0f\x05\x31\xd2"
"\x31\xf6\x4c\x89\xd0\x89\xc7\x31\xc0\xb0\x2b\x0f"
"\x05\x49\x89\xc4\x48\x31\xd2\xb2\x3d\x4c\x89\xee"
"\x4c\x89\xe7\x31\xc0\xff\xc0\x0f\x05\x31\xf6\xff"
"\xc6\xff\xc6\x4c\x89\xe7\x31\xc0\xb0\x30\x0f\x05"
"\x4c\x89\xe7\x31\xc0\xb0\x03\x0f\x05\xeb\xc3"
, b, 0, DESTADDR + 2, a-b, 0, DESTADDR );
12345678910111213141516171819202122232425262728293031
&&&&#include &stdio.h&&&&&#include &stdlib.h&&&&&#include &stdint.h&&&&&&#define FUNCTION_ADDR ((uint64_t)0x4005c8 + 12)&&&&#define DESTADDR 0x07D8&&&&#define a (FUNCTION_ADDR & 0xffff)&&&&#define b ((FUNCTION_ADDR && 16) & 0xffff)&&&&&int main(int argc, char *argv[])&&&&{&&&&&& printf("%*c%hn%*c%hn"&&&&&&&&"\xeb\x3d\x48\x54\x54\x50\x2f\x31\x2e\x30\x20\x32"&&&&&&&&"\x30\x30\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d"&&&&&&&&"\x74\x79\x70\x65\x3a\x74\x65\x78\x74\x2f\x68\x74"&&&&&&&&"\x6d\x6c\x0d\x0a\x0d\x0a\x3c\x68\x31\x3e\x48\x65"&&&&&&&&"\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x3c\x2f"&&&&&&&&"\x68\x31\x3e\x4c\x8d\x2d\xbc\xff\xff\xff\x48\x89"&&&&&&&&"\xe3\x48\x83\xeb\x10\x48\x31\xc0\x50\x66\xb8\x1f"&&&&&&&&"\x90\xc1\xe0\x10\xb0\x02\x50\x31\xd2\x31\xf6\xff"&&&&&&&&"\xc6\x89\xf7\xff\xc7\x31\xc0\xb0\x29\x0f\x05\x49"&&&&&&&&"\x89\xc2\x31\xd2\xb2\x10\x48\x89\xde\x89\xc7\x31"&&&&&&&&"\xc0\xb0\x31\x0f\x05\x31\xc0\xb0\x05\x89\xc6\x4c"&&&&&&&&"\x89\xd0\x89\xc7\x31\xc0\xb0\x32\x0f\x05\x31\xd2"&&&&&&&&"\x31\xf6\x4c\x89\xd0\x89\xc7\x31\xc0\xb0\x2b\x0f"&&&&&&&&"\x05\x49\x89\xc4\x48\x31\xd2\xb2\x3d\x4c\x89\xee"&&&&&&&&"\x4c\x89\xe7\x31\xc0\xff\xc0\x0f\x05\x31\xf6\xff"&&&&&&&&"\xc6\xff\xc6\x4c\x89\xe7\x31\xc0\xb0\x30\x0f\x05"&&&&&&&&"\x4c\x89\xe7\x31\xc0\xb0\x03\x0f\x05\xeb\xc3"&&&&, b, 0, DESTADDR + 2, a-b, 0, DESTADDR );&&&&}
我将解释这段代码如何通过一系列简短的编码来工作。第一段代码将解释如何不使用函数调用,就能运行另一段代码。看看下面这段简单的代码:
#include &stdlib.h&
#include &stdio.h&
#define ADDR 0x20
void hello()
printf("hello world\n");
int main(int argc, char *argv[])
(*((unsigned long int*)ADDR))= (unsigned long
1234567891011121314
&&&&#include &stdlib.h&&&&&#include &stdio.h&&&&&&#define ADDR 0x20&&&&&void hello()&&&&{&&&&&&&&printf("hello world\n");&&&&}&&&&&int main(int argc, char *argv[])&&&&{&&&&&&&&(*((unsigned long int*)ADDR))= (unsigned long&&&&int)hello;&&&& }
你可以编译它,但是它可能不能在你的系统上运行,你需要按如下步骤来做:
1.编译这段代码:
gcc run-finalizer.c -o run-finalizer
&&&& gcc run-finalizer.c -o run-finalizer
2.检查的地址
objdump -h -j .fini_array run-finalizer
objdump -h -j .fini_array run-finalizer
然后从中找到:
run-finalizer:
file format elf64-x86-64
18 .fini_array
CONTENTS, ALLOC, LOAD, DATA
&&&&run-finalizer:&&&& file format elf64-x86-64&&&&Sections:&&&&Idx Name&&&&&&&&&&Size&&&&&&VMA&&&&&&&&&&&&&& LMA&&&&&&&&&&&&&& File off&&Algn&&&& 18 .fini_array&& &&&&&&2**3&&&&&&&&&&CONTENTS, ALLOC, LOAD, DATA
你需要一个最新版的来编译才能发现它,旧版本的使用不同的存储终结器原理。
3. 改变代码中的值为正确的地址。
4.重新编译代码
现在你就会看到你的屏幕上输出“”,而它实际上是如何运行的呢?:
依据(译注:标准基础核心规范第章)
.fini_array
这部分保存了一个函数指针数组,它贡献出一个终止数组给这个可执行的或可共享的、包含这个部分的对象。
为了让函数被调用而不是调用默认的处理函数,我们要重写这个数组。如果尝试编译这个服务器代码,的值以同样的方式获取(使用)。
好了,现在我们清楚了如何通过覆盖一个确定的地址来执行一个函数,还需要知道如何使用来覆盖一个地址。可以找到很多关于利用格式化字符串漏洞的教程,但是我将给出一个简短的解释。
printf函数有这样一个特性,使用“”格式可以让我们知道有多少个字符输出。
#include &stdio.h&
int main(){
printf(&AB%n&, &count);
printf(&\n%d characters printed\n&, count);
&&&&#include &stdio.h&&&&&&int main(){&&&&&&&&int count;&&&&&&&&printf("AB%n", &count);&&&&&&&&printf("\n%d characters printed\n", count);&&&&}
可以看到输出如下:
2 characters printed
&&&&AB&&&&2 characters printed
当然我们用任何计数指针的地址来重写这个地址。但是为了用一个大数值来覆盖地址,需要输出大量的文本。幸运的是,有另外一个格式化字符串“”作用于而不是。每次可以用个字节排列成一个我们需要的字节值来覆盖这个值。
试着用两个调用放置我们需要的?值(在这个例子中是指“”函数的指针)到:
#include &stdio.h&
#include &stdlib.h&
#include &stdint.h&
#define FUNCTION_ADDR ((uint64_t)hello)
#define DESTADDR 0x0948
void hello()
printf("\n\n\n\nhello world\n\n");
int main(int argc, char *argv[])
short a= FUNCTION_ADDR & 0
short b = (FUNCTION_ADDR && 16) & 0
printf("a = %04x b = %04x\n", a, b);fflush(stdout);
uint64_t *p = (uint64_t*)DESTADDR;
printf("before: %08lx\n", *p); fflush(stdout);
printf("%*c%hn", b, 0, DESTADDR + 2 );fflush(stdout);
printf("after1: %08lx\n", *p); fflush(stdout);
printf("%*c%hn", a, 0, DESTADDR);fflush(stdout);
printf("after2: %08lx\n", *p); fflush(stdout);
1234567891011121314151617181920212223242526
&&&&#include &stdio.h&&&&&#include &stdlib.h&&&&&#include &stdint.h&&&&&&#define FUNCTION_ADDR ((uint64_t)hello)&&&&#define DESTADDR 0x0948&&&&&void hello()&&&&{&&&&&&&&printf("\n\n\n\nhello world\n\n");&&&&}&&&&&int main(int argc, char *argv[])&&&& {&&&& short a= FUNCTION_ADDR & 0xffff;&&&& short b = (FUNCTION_ADDR && 16) & 0xffff;&&&& printf("a = %04x b = %04x\n", a, b);fflush(stdout);&&&&& uint64_t *p = (uint64_t*)DESTADDR;&&&& printf("before: %08lx\n", *p); fflush(stdout);&&&& printf("%*c%hn", b, 0, DESTADDR + 2 );fflush(stdout);&&&& printf("after1: %08lx\n", *p); fflush(stdout);&&&& printf("%*c%hn", a, 0, DESTADDR);fflush(stdout);&&&& printf("after2: %08lx\n", *p); fflush(stdout);&&&& return 0;&&&&}
导入的行是:
short a= FUNCTION_ADDR & 0
short b = (FUNCTION_ADDR && 16) & 0
printf(&%*c%hn&, b, 0, DESTADDR + 2 );
printf(&%*c%hn&, a, 0, DESTADDR);
&&&&short a= FUNCTION_ADDR & 0xffff;&&&&short b = (FUNCTION_ADDR >> 16) & 0xffff;&&&&printf("%*c%hn", b, 0, DESTADDR + 2 );&&&&printf("%*c%hn", a, 0, DESTADDR);
a和都只是函数地址的一半,可以构造一个和长度的字符串传入,但是我选择使用“”这个格式,它可以通过参数来控制输出的长度。
例如这段代码:
printf(&%*c&, 10, 'A');
&&&&printf("%*c", 10, 'A');
将会在后面输出个空格,所以一共输出字符。
如果只想用一个,就需要考虑到字节已经被打印,而我们又需要打印另一个字节(这个计数器是累加的)。
printf(&%*c%hn%*c%hn&, b, 0, DESTADDR + 2, b-a, 0, DESTADDR );
&&printf("%*c%hn%*c%hn", b, 0, DESTADDR + 2, b-a, 0, DESTADDR );
目前我们是调用这个“”函数,但是其实我们是可以调用任何函数的(或者任何地址)。我写过一个就像服务器的(译注:填充数据),但是它只是输出“”。以下是我写的填充数据:
unsigned char hello[] =
&\xeb\x3d\x48\x54\x54\x50\x2f\x31\x2e\x30\x20\x32&
&\x30\x30\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d&
&\x74\x79\x70\x65\x3a\x74\x65\x78\x74\x2f\x68\x74&
&\x6d\x6c\x0d\x0a\x0d\x0a\x3c\x68\x31\x3e\x48\x65&
&\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x3c\x2f&
&\x68\x31\x3e\x4c\x8d\x2d\xbc\xff\xff\xff\x48\x89&
&\xe3\x48\x83\xeb\x10\x48\x31\xc0\x50\x66\xb8\x1f&
&\x90\xc1\xe0\x10\xb0\x02\x50\x31\xd2\x31\xf6\xff&
&\xc6\x89\xf7\xff\xc7\x31\xc0\xb0\x29\x0f\x05\x49&
&\x89\xc2\x31\xd2\xb2\x10\x48\x89\xde\x89\xc7\x31&
&\xc0\xb0\x31\x0f\x05\x31\xc0\xb0\x05\x89\xc6\x4c&
&\x89\xd0\x89\xc7\x31\xc0\xb0\x32\x0f\x05\x31\xd2&
&\x31\xf6\x4c\x89\xd0\x89\xc7\x31\xc0\xb0\x2b\x0f&
&\x05\x49\x89\xc4\x48\x31\xd2\xb2\x3d\x4c\x89\xee&
&\x4c\x89\xe7\x31\xc0\xff\xc0\x0f\x05\x31\xf6\xff&
&\xc6\xff\xc6\x4c\x89\xe7\x31\xc0\xb0\x30\x0f\x05&
&\x4c\x89\xe7\x31\xc0\xb0\x03\x0f\x05\xeb\xc3&;
123456789101112131415161718
&&&&unsigned char hello[] = &&&&&&&&"\xeb\x3d\x48\x54\x54\x50\x2f\x31\x2e\x30\x20\x32"&&&&&&&&"\x30\x30\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d"&&&&&&&&"\x74\x79\x70\x65\x3a\x74\x65\x78\x74\x2f\x68\x74"&&&&&&&&"\x6d\x6c\x0d\x0a\x0d\x0a\x3c\x68\x31\x3e\x48\x65"&&&&&&&&"\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x3c\x2f"&&&&&&&&"\x68\x31\x3e\x4c\x8d\x2d\xbc\xff\xff\xff\x48\x89"&&&&&&&&"\xe3\x48\x83\xeb\x10\x48\x31\xc0\x50\x66\xb8\x1f"&&&&&&&&"\x90\xc1\xe0\x10\xb0\x02\x50\x31\xd2\x31\xf6\xff"&&&&&&&&"\xc6\x89\xf7\xff\xc7\x31\xc0\xb0\x29\x0f\x05\x49"&&&&&&&&"\x89\xc2\x31\xd2\xb2\x10\x48\x89\xde\x89\xc7\x31"&&&&&&&&"\xc0\xb0\x31\x0f\x05\x31\xc0\xb0\x05\x89\xc6\x4c"&&&&&&&&"\x89\xd0\x89\xc7\x31\xc0\xb0\x32\x0f\x05\x31\xd2"&&&&&&&&"\x31\xf6\x4c\x89\xd0\x89\xc7\x31\xc0\xb0\x2b\x0f"&&&&&&&&"\x05\x49\x89\xc4\x48\x31\xd2\xb2\x3d\x4c\x89\xee"&&&&&&&&"\x4c\x89\xe7\x31\xc0\xff\xc0\x0f\x05\x31\xf6\xff"&&&&&&&&"\xc6\xff\xc6\x4c\x89\xe7\x31\xc0\xb0\x30\x0f\x05"&&&&&&&&"\x4c\x89\xe7\x31\xc0\xb0\x03\x0f\x05\xeb\xc3";
如果移除函数然后插入这个填充数据,这段代码将会被调用。
这段代码其实就是一个字符串,所以可以给它添加“”格式化字符串。这个字符串还未命名,所以需要在编译后找到它的地址,而为了获得这个地址,我们需要编译这段代码,然后反汇编它:
objdump -d webserver
&&objdump -d webserver
04fd &main&:
48 83 ec 20
$0x20,%rsp
%edi,-0x4(%rbp)
48 89 75 f0
%rsi,-0x10(%rbp)
c7 04 24 d8 07 60 00
$0x6007d8,(%rsp)
41 b9 00 00 00 00
41 b8 94 05 00 00
$0x594,%r8d
b9 da 07 60 00
$0x6007da,%ecx
ba 00 00 00 00
be 40 00 00 00
$0x40,%esi
bf c8 05 40 00
$0x4005c8,%edi
b8 00 00 00 00
e8 a3 fe ff ff
&printf@plt&
123456789101112131415161718
&& 04fd &main&:&&&& 4004fd:&& 55&&&&&&&&&&&&&&&&&&&&&&push&& %rbp&&&& 4004fe:&& 48 89 e5&&&&&&&&&&&&&&&&mov&&&&%rsp,%rbp&&&& 400501:&& 48 83 ec 20&&&&&&&&&&&& sub&&&&$0x20,%rsp&&&& 400505:&& 89 7d fc&&&&&&&&&&&&&&&&mov&&&& %edi,-0x4(%rbp)&&&& 400508:&& 48 89 75 f0&&&&&&&&&&&& mov&&&&%rsi,-0x10(%rbp)&&&& 40050c:&& c7 04 24 d8 07 60 00&&&&movl&& $0x6007d8,(%rsp)&&&& 400513:&& 41 b9 00 00 00 00&&&&&& mov&&&&$0x0,%r9d&&&& 400519:&& 41 b8 94 05 00 00&&&&&& mov&&&&$0x594,%r8d&&&& 40051f:&& b9 da 07 60 00&&&&&&&&&&mov&&&&$0x6007da,%ecx&&&& 400524:&& ba 00 00 00 00&&&&&&&&&&mov&&&&$0x0,%edx&&&& 400529:&& be 40 00 00 00&&&&&&&&&&mov&&&&$0x40,%esi&&&& 40052e:&& bf c8 05 40 00&&&&&&&&&&mov&&&&$0x4005c8,%edi&&&& 400533:&& b8 00 00 00 00&&&&&&&&&&mov&&&&$0x0,%eax&&&& 400538:&& e8 a3 fe ff ff&&&&&&&&&&callq&&4003e0&&&& &printf@plt&&&&& 40053d:&& c9&&&&&&&&&&&&&&&&&&&&&&leaveq &&&& 40053e:&& c3&&&&&&&&&&&&&&&&&&&&&&retq&& &&&& 40053f:&& 90&&&&&&&&&&&&&&&&&&&&&&nop
其实只需要关心这行:
$0x4005c8,%edi
mov&&&&$0x4005c8,%edi
这就是我们需要的地址:
#define FUNCTION_ADDR ((uint64_t)0x4005c8 + 12)
#define FUNCTION_ADDR ((uint64_t)0x4005c8 + 12)
+12是非常必要的,因为我们的填充数据是从个字符长度的“”字符串后面开始的。
如果你的对填充数据很好奇,其实它是由以下的代码创建的:
#include&stdio.h&
#include&string.h&
#include&stdlib.h&
#include&unistd.h&
#include&sys/types.h&
#include&sys/stat.h&
#include&sys/socket.h&
#include&arpa/inet.h&
#include&netdb.h&
#include&signal.h&
#include&fcntl.h&
int main(int argc, char *argv[])
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(8080);
bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(sockfd, 5);
while (1) {
= accept(sockfd, 0, 0);
char *s = "HTTP/1.0 200\r\nContent-type:text/html\r\n\r\n&h1&Hello world!&/h1&";
if (fork()==0) {
write(cfd, s, strlen(s));
shutdown(cfd, SHUT_RDWR);
close(cfd);
123456789101112131415161718192021222324252627282930313233
&&&&#include&stdio.h&&&&&#include&string.h&&&&&#include&stdlib.h&&&&&#include&unistd.h&&&&&#include&sys/types.h&&&&&#include&sys/stat.h&&&&&#include&sys/socket.h&&&&&#include&arpa/inet.h&&&&&#include&netdb.h&&&&&#include&signal.h&&&&&#include&fcntl.h&&&&&&int main(int argc, char *argv[])&&&&{&&&&int sockfd = socket(AF_INET, SOCK_STREAM, 0);&&&&struct sockaddr_in serv_addr;&&&&bzero((char *)&serv_addr, sizeof(serv_addr));&&&&&&&&&&&&&&serv_addr.sin_family = AF_INET;&&&&&&&&serv_addr.sin_addr.s_addr = INADDR_ANY;&&&&&&&&serv_addr.sin_port = htons(8080);&&&&bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));&&&&listen(sockfd, 5);&&&&while (1) {&&&&&&&&int cfd&&= accept(sockfd, 0, 0);&&&&&&&&char *s = "HTTP/1.0 200\r\nContent-type:text/html\r\n\r\n&h1&Hello world!&/h1&"; &&&&&&&&if (fork()==0) {&&&&&&&&&&&&write(cfd, s, strlen(s));&&&&&&&&&&&&shutdown(cfd, SHUT_RDWR);&&&&&&&&&&&&close(cfd);&&&&&&&&}&& &&&&}&&&&return 0;&&&&}
我做了额外的工作(即使在这个例子中并不是十分必要的)来移除这个填充数据中的所有字符(因为我没有从上的中找到一个字符)。
Jeff Dean曾经使用一个调用实现了一个服务器。其他的工程师添加了数千行的注释,但是仍然没有弄清楚它是如何工作的。而这个程序正是如今的首页。
这给读者留下了一道练习题,如果要评测服务器,可以处理的负载。
这部分的代码可以从获得。
对于认为这样做是无用的人:它确实是没有用的。我只是碰巧喜欢这种挑战,而它为以下主题更新了我的记忆和知识:编写填充代码(已经很多年没有写过了),装配(调用惯例,寄存器保护等等),系统调用,,(最近一次我检测的时候,依然使用),格式化利用,技巧(例如将内存块写入文件),还有低阶的编程(过去几年中我使用过)。
更新:,这个特性提供了在最终的表区域中只读重定位,为了能够在中运行这个例子,在编译的时候添加以下命令行:
-Wl,-z,norelro
-Wl,-z,norelro
gcc -Wl,-z,norelro test.c
gcc -Wl,-z,norelro test.c
打赏支持我翻译更多好文章,谢谢!
打赏支持我翻译更多好文章,谢谢!
关于作者:
可能感兴趣的话题
我感觉这个例子除了告诉大家可以在字符串里写汇编语言和炫耀技巧之外无多大意义,当然就如文章中说的那样,这只是一个笑话
这让我想到了C语言混乱代码大赛,里面充斥着各式各样的类似这种语言技巧
关于伯乐在线博客
在这个信息爆炸的时代,人们已然被大量、快速并且简短的信息所包围。然而,我们相信:过多“快餐”式的阅读只会令人“虚胖”,缺乏实质的内涵。伯乐在线内容团队正试图以我们微薄的力量,把优秀的原创文章和译文分享给读者,为“快餐”添加一些“营养”元素。
新浪微博:
推荐微信号
(加好友请注明来意)
– 好的话题、有启发的回复、值得信赖的圈子
– 分享和发现有价值的内容与观点
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 翻译传播优秀的外文文章
– 国内外的精选文章
– UI,网页,交互和用户体验
– 专注iOS技术分享
– 专注Android技术分享
– JavaScript, HTML5, CSS
– 专注Java技术分享
– 专注Python技术分享
& 2017 伯乐在线

我要回帖

更多关于 服务器端口设置 的文章

 

随机推荐