一,介绍

比赛题目很简单:构造一个程序,在 stdout 上打印出自身的 MD5,程序越短越好。按最终程序文件大小字节数排名,文件越小,排名越靠前。

只能使用 ld-linux-x86-64.so, libc.so, libdl.so, libgcc_s.so, libm.so, libstdc++.so 。

禁止了 socket, shmget, fork, execvc 等 syscall 。

汇编高手如云,本人只做到 752 字节,只拿到 27 名。

但忙活好几天,学到不少东西,也有苦劳,还是值得记录一下。

基本是纯 C 实现,没有动用汇编。

二,用过的一些思路和资源

基于这个 MD5 实现改造

https://gist.github.com/creationix/4710780

https://www.nayuki.io/page/fast-md5-hash-implementation-in-x86-assembly

参照 musl 和 rt0 ,去除对 glibc 依赖

  1. musl libc https://www.musl-libc.org/
  2. rt0 : https://github.com/lpsantil/rt0

三,没有成功的一些思路

  1. 思路,利用内核的 MD5 计算代码:

    grep -i md5 linux-5.6.2

    只找到 2处,

    1. TCP_MD5SIG , CONFIG_TCP_MD5SIG : "TCP: MD5 Signature Option support (RFC2385)"

      一个冷门特性,看起来通过 socket 才能执行。

    2. AF_ALG Linux 的 crypto api,通过 socket 形式暴露 api, 但是 socket 被禁用了。

      https://www.chronox.de/libkcapi.html

  2. 构造碰撞,hardcode 直接 print 出来

    https://github.com/corkami/collisions

    https://www.rogdham.net/2017/03/12/gif-md5-hashquine.en

    https://github.com/Rogdham/gif-md5-hashquine

    http://www.win.tue.nl/hashclash/

    尝试了 fastcoll_v1 ,但是 fastcoll 构造1个字节的冲突要 2 block = 128 字节 ,则 16 字节就要 2048 字节,还不如 C 语言直接算。

  3. 通过某些 ipc 机制,绕过评判。

    试了简单的 shmget, 不成功。

三. 代码

后续借鉴思路,有优化

#define SYS_write 1
#define SYS_exit 60 inline long syscall3(long n, long a0, long a1, long a2) {
long ret;
__asm__ volatile("syscall" : "=a"(ret) : "a"(n), "D"(a0), "S"(a1), "d"(a2) : "rcx", "r11", "memory");
return (ret);
}
inline long syscall1(long n, long a0) {
long ret;
__asm__ volatile("syscall" : "=a"(ret) : "a"(n), "D"(a0) : "rcx", "r11", "memory");
return (ret);
} static int write(int f, const char* d, int l) {
int ret = syscall3(SYS_write, f, (long)(d), l);
return (ret);
} void _exit(int r) { syscall1(SYS_exit, r); } typedef unsigned uint32_t;
typedef unsigned char uint8_t;
typedef unsigned long size_t;
#define NULL 0 // In words #define memcpy(dest, src, n) \
for (size_t i = 0; i < n; ++i) { \
((char*)dest)[i] = ((const char*)src)[i]; \
} // leftrotate function definition
#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) static void md5_hash(uint8_t* initial_msg, const size_t initial_len, uint32_t hash[4]) {
// Note: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating // r specifies the per-round shift amounts
const static uint8_t r[] = {
7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21,
}; // Use binary integer part of the sines of integers (in radians) as constants// Initialize variables:
uint32_t k[64];
for (int i = 0; i < 64; ++i) {
double f = 1.0 + i;
asm("fsin;\n\t"
"fabs;\n\t"
: "=t"(f)
: "0"(f));
f *= 4294967296.0;
k[i] = (uint32_t)f;
} // Pre-processing: adding a single 1 bit
// append "1" bit to message
/* Notice: the input bytes are considered as bits strings,
where the first bit is the most significant bit of the byte.[37] */ // Pre-processing: padding with zeros
// append "0" bit until message length in bit ≡ 448 (mod 512)
// append length mod (2 pow 64) to message const int new_len = ((((initial_len + 8) / 64) + 1) * 64) - 8; // Message (to prepare)
uint8_t* msg = initial_msg msg[initial_len] = 128; // write the "1" bit uint32_t bits_len = 8 * initial_len; // note, we append the len
memcpy(msg + new_len, &bits_len, 4); // in bits at the end of the buffer // Process the message in successive 512-bit chunks:
// for each 512-bit chunk of message:
for (int offset = 0; offset < new_len; offset += 64) {
// break chunk into sixteen 32-bit words w[j], 0 ≤ j ≤ 15
uint32_t* w = (uint32_t*)(msg + offset); // Initialize hash value for this chunk:
uint32_t a = hash[0];
uint32_t b = hash[1];
uint32_t c = hash[2];
uint32_t d = hash[3]; // Main loop:
uint32_t i;
for (i = 0; i < 64; i++) {
uint32_t f, g; if (i < 16) {
f = (b & c) | ((~b) & d);
g = i;
} else if (i < 32) {
f = (d & b) | ((~d) & c);
g = (5 * i + 1) % 16;
} else if (i < 48) {
f = b ^ c ^ d;
g = (3 * i + 5) % 16;
} else {
f = c ^ (b | (~d));
g = (7 * i) % 16;
} uint32_t temp = d;
d = c;
c = b;
b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i / 4 / 4 * 4 + i % 4]);
a = temp;
} // Add this chunk's hash to result so far: hash[0] += a;
hash[1] += b;
hash[2] += c;
hash[3] += d;
}
} static char xdigits(char c) {
if ((c >= 0) && (c <= 9)) {
return '0' + c;
}
return 'a' + (c - 10);
} void _start(void) __attribute__((noreturn)); void _start(void) {
uint32_t hash[4] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476}; char* addr = (char*)0x400000;
const size_t len = 688;
md5_hash(addr, len, hash); unsigned char* md = (unsigned char*)&hash[0];
char buff[32];
for (int i = 0; i < 32; ++i) {
unsigned x = md[(i >> 1)];
x >>= (i & 1 ? 0 : 4);
buff[i] = xdigits(x & 15);
}
write(1, &buff[0], sizeof(buff));
_exit(0);
}
CFLAGS="-static -Os -Wl,--omagic -D__RT0_WITH_FASTER_SYSCALL__ -ffunction-sections -Wl,--gc-sections -nostartfiles -nodefaultlibs -nostdlib -nostdinc -Wl,--build-id=none -fomit-frame-pointer -fno-stack-protector -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops -fmerge-all-constants -fno-ident -mfpmath=sse -mfancy-math-387  -finline-functions-called-once -I ./ "

gcc   $CFLAGS  print_my_md5.c  -S -fverbose-asm
gcc $CFLAGS print_my_md5.s -o print_my_md5 strip -R .comment print_my_md5
strip -R .bss print_my_md5
ls -l ./print_my_md5
./ELFkickers/sstrip/sstrip print_my_md5
./print_my_md5
echo
md5sum ./print_my_md5
ls -l ./print_my_md5

构造最短程序打印自身的 MD5的更多相关文章

  1. 如何用C#语言构造蜘蛛程序

    "蜘蛛"(Spider)是Internet上一种很有用的程序,搜索引擎利用蜘蛛程序将Web页面收集到数据库,企业利用蜘蛛程序监视竞争对手的网站并跟踪变动,个人用户用蜘蛛程序下载We ...

  2. .Net_用控制台程序打印指定行数的三角型(面试题)

    .Net_用控制台程序打印指定行数的三角型(面试题)   下面是一个由*号组成的4行倒三角形图案.要求: 1.输入倒三角形的行数,行数的取值3-21之间,对于非法的行数,要求抛出提示“非法行数!”: ...

  3. 代码实现从键盘接收一个字符串, 程序对其中所有字符进行排序,例如键盘输入: helloitcast程序打印:acehillostt

    package com.loaderman.test; import java.util.Comparator; import java.util.Scanner; import java.util. ...

  4. java例题_47 读取 7 个数(1—50)的整数值,每读取一个值,程序打印出该值个数的*

    1 /*47 [程序 47 打印星号] 2 题目:读取 7 个数(1-50)的整数值,每读取一个值,程序打印出该值个数的*. 3 */ 4 5 /*分析 6 * 1.多次读取---for循环 7 * ...

  5. 微信小程序下可以使用的MD5以及AES加密(通用)

    两段代码都来自网络 ,在小程序下的加解密结果与CS/BS等算出的结果都一致,支持汉字. 一.MD5: var rotateLeft = function (lValue, iShiftBits) { ...

  6. 微信小程序 使用HMACSHA1和md5为登陆注册报文添加指纹验证签名

    对接口请求报文作指纹验证签名相信在开发中经常碰到, 这次在与java后端一起开发小程序时,就碰到需求对登陆注册请求报文添加指纹验证签名来防止信息被修改 先来看下我们与后端定制签名规则 2.4. 签名规 ...

  7. winfor应用程序打印报表清单

    最近一周竟然有2位以前的同事问我在winfor应用程序里面打印怎么搞,所以才有了写这篇文章的打算,索性现在没事就写出来 在窗体上简单的布局设置一下如图 定义一个Model 我在里面放了属性之外还从写了 ...

  8. CF1005F Berland and the Shortest Paths (树上构造最短路树)

    题目大意:给你一个边权为$1$的无向图,构造出所有$1$为根的最短路树并输出 性质:单源最短路树上每个点到根的路径 ,一定是这个点到根的最短路之一 边权为$1$,$bfs$出单源最短路,然后构建最短路 ...

  9. 微信小程序打印json log

    微信小程序中如果 res.data数据是一个json格式数据.console.log("===data===" + res.data);//如果这样打印出了是只会打印一个对象名称, ...

随机推荐

  1. JavaScript new 的时候到底发生了什么?

    function Person(name) { this.name = name; } let liLei = new Person('lilei'); console.log(liLiei.name ...

  2. [原创] 关于步科eview人机界面HMI的使用 - HMI做Slave - Modbus RS485通讯

    做测试设备,或者自动化设备常常用到HMI 触摸屏 我有个案子用到了 步科的eview 触摸屏 型号 ET070 我的是单片机主板 控制 HMI显示,通讯用485  MODBUS 单片机板充当 主控 , ...

  3. 「给产品经理讲JVM」:垃圾收集算法

    纠结的我,给我的JVM系列终于起了第三个名字,害,我真是太难了.从 JVM 到 每日五分钟,玩转 JVM 再到现在的给产品经理讲 JVM ,虽然内容为王,但是标题可以让更多的人看到我的文章,所以,历经 ...

  4. Linux网络安全篇,FTP服务器的架设

    一.FTP简介 FTP基于TCP协议.而且FTP服务器使用了命令通道和数据流通道两个连接.两个连接都会分别进行三次握手.在命令通道中客户端会随机取一个大于1024的端口与FTP服务器的21端口建立连接 ...

  5. Linux(Fedora)系统下配制8086汇编环境

    1.到www,nasm.us下载nasm 2.解压并安装nasm #tar -xzvf nasm-2.11.08.tar.gz #cd nasm-2.11.08 #./configure #make ...

  6. Python爬虫 ---scrapy框架初探及实战

    目录 Scrapy框架安装 操作环境介绍 安装scrapy框架(linux系统下) 检测安装是否成功 Scrapy框架爬取原理 Scrapy框架的主体结构分为五个部分: 它还有两个可以自定义下载功能的 ...

  7. 谁说.NET不适合搞大数据,机器学习、人工智能

    SciSharp Stack SciSharp STACK: https://scisharp.github.io/SciSharp/ 基于.NET的开源生态系统,用于数据科学.机器学习和AI. Sc ...

  8. Springboot启动流程简单分析

    springboot启动的类为SpringApplication,执行构造函数初始化属性值后进入run方法: 然后返回ConfigurableApplicationContext(spring应用). ...

  9. 第一章:shell脚本初入门

    1.shell脚本中的source或者.空格再加上文件,表示加载文件中的命令及语句(困惑多时终于解开^-^) 2.脚本开头书写好作者版本等信息,方便维护:流程语句提前把格式写好,防止遗漏 3.定义字符 ...

  10. JavaScript实现简单的弹幕效果实例分析

    不知大家有没有感受到,弹幕又是另一出好戏!! 不过我个人还是比较排斥看电视的时候被出来的弹幕打扰.今天我们来写一个简单的弹幕.简单到什么程度呢?看下效果: 由图可以看出,我们的呆毛html结构确实是非 ...