一,介绍

比赛题目很简单:构造一个程序,在 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. dp例题01. 任务价值最大化

    题目Description: 大凯有n项任务可选择去做, 分别对应有开始时间, 结束时间以及任务报酬, 同一时间内最多做一件任务, 现在大凯想知道最多能得到多少报酬, 于是把求解任务交给了你. 输入: ...

  2. MySql 分组函数

    #二.分组函数/*功能:用作统计使用,又称为聚合函数或统计函数或组函数 分类:sum 求和.avg 平均值.max 最大值 .min 最小值 .count 计算个数 特点:1.sum.avg一般用于处 ...

  3. 五、【Docker笔记】Dockers仓库

    仓库是集中存放镜像的地方,仓库的概念不要与注册服务器做混淆.注册服务器是存放仓库的具体服务器,每个服务器上可能有多个仓库,一个仓库有多个镜像. 仓库又可分为共有仓库和私有仓库,最大的共有仓库即Dock ...

  4. Spring之Bean的管理方式(Content,Beans)

    Spring的bean管理(注释) 注解 代码里特殊的标记,使用注解也可以直接完成相关功能 注解写法:@注解名称(属性名=属性值) 使用在类,方法,属性上面 Spring注解开发准备 导入jar包 ( ...

  5. css中border-sizing属性详解和应用

    box-sizing用于更改用于计算元素宽度和高度的默认的 CSS 盒子模型.它有content-box.border-box和inherit三种取值.inherit指的是从父元素继承box-sizi ...

  6. JavaScript RegExp.​$1...$9 属性详解

    RegExp.$1...$9属性用于返回正则表达式模式中某个子表达式匹配的文本. 正则表达式中每个小括号内的部分表达式就是一个子表达式. 该属性是RegExp全局对象的一个只读属性,所有主流浏览器均支 ...

  7. PTA数据结构与算法题目集(中文) 7-10

    PTA数据结构与算法题目集(中文)  7-10 7-10 公路村村通 (30 分)   现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低 ...

  8. c期末笔记(4)

    未命名易错点 1.0<y<10 在c语言中,0<y<10不是一个正确的表达式,应该利用逻辑运算符替换(y>0&&y<10或者!(y<=0||y ...

  9. 34.4 对象流 ObjectOutputStream ObjectInputStream

    * 对象操作流:可以用于读写任意类型的对象 * ObjectOutputStream * writeObject * ObjectOutputStream(OutputStream out) * Ob ...

  10. lr使用soap协议,来对webservice接口进行测试

    实际项目中基于WSDL来测试WebService的情况并不多,WSDL并不是WebService测试的最佳选择. 最主要的原因还是因为WSDL文档过于复杂. 在案例(天气预报WebService服务) ...