一,介绍

比赛题目很简单:构造一个程序,在 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. 白话web安全

    伤心往事 梦回大二,那时候沉迷于毒奶粉,甚至国庆都在宿舍与毒奶粉共同度过,但是却发生了一件让我迄今难忘的事情~ 我新练的黑暗武士被盗了!!!干干净净!!! 虽然过了好久了,但是记忆犹新啊,仿佛发生在昨 ...

  2. [斯坦福大学2014机器学习教程笔记]第五章-控制语句:for,while,if语句

    在本节中,我们将学习如何为Octave程序写控制语句. 首先,我们先学习如何使用for循环.我们将v设为一个10行1列的零向量. 接着,我们写一个for循环,让i等于1到10.写出来就是for i = ...

  3. SaaS架构(一) 弱后端强前端的尝试和问题

    最近在公司项目组内部沙龙的时候,提出一个"弱后端强前端"的概念,其实已经在项目内部新的服务有做试点,我们整个SaaS系统,后端主要是JAVA构建,前端是Angular构建.&quo ...

  4. MySQL服务使用cmd启动与停止服务

    MySQL未设置自动启动,在使用时需要手动打开服务,方法如下 mysql服务的启动: 以管理员的身份运行cmd命令窗口,输入命名 net start mysql 提示:必须使用管理员身份运行cmd 如 ...

  5. ElementUI el-input标签 绑定keyup事件v-on:keyup.enter="convert"无效解决方案

    期望实现,输入数字后,回车直接执行点击按钮"转换" 无效写法: <el-input v-model="input" placeholder="请 ...

  6. html5调用手机摄像头

    <input type="file" accept="image/*" capture="camera"><input t ...

  7. 《mysql 必知必会》 速查指南

    目录 增 添加一整行 插入多行 删 删除指定行 删除所有行 改 查 简单检索 结果筛选 结果排序 结果过滤 创建字段 处理函数 数据分组 其他高级用法 文章内容均出自 <MySQL 必知必会&g ...

  8. 30.5 Map遍历方法

    package day30_2_Map; import java.util.HashMap; import java.util.Map; import java.util.Set; /* 方法一.用e ...

  9. pgsql中json格式数组查询结果变成了字符串

    场景复原 最近使用到了json的数组,用来存储多个文件的值,发现在连表查询的时候返回结果变成了字符串. { "id": "repl-placeholder-007&quo ...

  10. tf.nn.bias_add 激活函数

    tf.nn.bias_add(value,bias,data_format=None,name=None) 参数: value:一个Tensor,类型为float,double,int64,int32 ...