前言

在ctf比赛中, 有时我们需要对可执行文件进行patch, 或者在植入后门时,patch也是常用的手段。不过手工patch比较麻烦,下面介绍几个工具。本文介绍遇到的各种技术,不断更新。

ELF

Patchkit

地址:

https://github.com/lunixbochs/patchkit.git

1.由于链接器的原因暂时还不能使用 libc 中的函数,所以所有要做的事情都需要我们自己实现。用 c 或者 asm

  • pt.patch(addr,jmp=jmp_addr) 用于修改程序流程。
  • pt.hook(addr, target) 用于劫持程序流程,进行参数过滤。

使用方式:./patch binary_file patch.py

过滤printf中 %n 的脚本。

def replace_free(pt):
printf_addr = 0x400548;// call printf 时的地址
new_printf = pt.inject(c=r'''
void fix_printf(char *fmt) {
for (int i = 0; fmt[i]; ++i)
{
if (fmt[i] == '%' && fmt[i+1] == 'n') {
//找到后,通过前移的方式删除字符,每次删掉一个。
int len=0;
int j;
while(fmt[len++]){
}
for(j=i;j<len-1;j++)
fmt[j] = fmt[j+1];
fmt[len-1] = '\x00';
len=0;
while(fmt[len++]){
}
for(j=i;j<len-1;j++)
fmt[j] = fmt[j+1]; fmt[len-1] = '\x00';
//i--;
}
} }
''')
pt.hook(printf_addr, new_printf);

64位程序,修改 malloc函数的参数为 0x20

def replace(pt):
malloc_addr = 0x040057A; //call malloc的位置
new_malloc = pt.inject(asm=r'''
mov rdi,0x20
ret
''')
pt.hook(malloc_addr, new_malloc);

32位,由于与栈进行操作,要注意保存还原返回地址

def replace(pt):
malloc_addr = 0x08048454;
new_malloc = pt.inject(asm=r'''
pop eax
pop ebx
push 0x20
push eax
ret
''')
pt.hook(malloc_addr,new_malloc);

或者

def replace(pt):
malloc_addr = 0x08048454;
new_malloc = pt.inject(asm=r'''
mov eax,0x20
mov [esp+4], eax
ret
''')
pt.hook(malloc_addr,new_malloc);

LIEF

程序地址:https://github.com/lief-project/LIEF

使用这个工具可以很方便的 patch elfpeMachO 文件。本文以 elf 为例。

通过交换导入导出符号

首先看第一个测试程序:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
puts("/bin/sh");
return EXIT_SUCCESS;
}

我们的目标是让他调用 puts 变成调用 system

方案一

修改 libc 中的相关符号,然后使用 LD_LIBRARY_PATH 加载我们修改后的库。

import lief

hashme = lief.parse("hashme")
libc = lief.parse("/lib/x86_64-linux-gnu/libc-2.23.so") # get puts, system symbol
puts_sym = filter(lambda e: e.name == "puts", libc.dynamic_symbols)[0]
system_sym = filter(lambda e: e.name == "system", libc.dynamic_symbols)[0] # swap them
puts_sym.name = "system"
system_sym.name = "puts"
libc.write("libc.so.6") print("done")

首先拿到 puts 和 system 符号对象,然后交换他们的名称。

成功

方案二

直接修改目标文件的导入符号,代码如下

import lief

hashme = lief.parse("hashme")

# get puts, system symbol
puts_sym = filter(lambda e: e.name == "puts", hashme.imported_symbols)[0] # set puts to system
puts_sym.name = "system" hashme.write("hashme.patch")
print("done")

直接增加代码进行patch

修改库函数

测试程序:

#include <stdio.h>
#include <stdlib.h>
#include <math.h> int main(int argc, char **argv) {
if (argc != 2) {
printf("Usage: %s <a> \n", argv[0]);
exit(-1);
} int a = atoi(argv[1]);
printf("exp(%d) = %f\n", a, exp(a));
return 0;
}

目标是hook exp 函数,直接增加一个 segments , 然后劫持函数指针到这里。首先编译一个 lib 用来提供用于 hook 的代码。

gcc -Os -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook

hook.c 的内容:

double hook(double x) {
return x + 100;
}

然后看脚本内容,很清晰。


import lief libm = lief.parse("/lib/x86_64-linux-gnu/libm-2.23.so")
hook = lief.parse("hook") segment_added = libm.add(hook.segments[0]) print("Hook inserted at VA: 0x{:06x}".format(segment_added.virtual_address)) exp_symbol = libm.get_symbol("exp")
hook_symbol = hook.get_symbol("hook") exp_symbol.value = segment_added.virtual_address + hook_symbol.value libm.write("libm.so.6")

通过 got/plt 表 直接劫持程序

测试程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h> // Damn_YoU_Got_The_Flag
char password[] = "\x18\x3d\x31\x32\x03\x05\x33\x09\x03\x1b\x33\x28\x03\x08\x34\x39\x03\x1a\x30\x3d\x3b"; inline int check(char* input); int check(char* input) {
for (int i = 0; i < sizeof(password) - 1; ++i) {
password[i] ^= 0x5c;
}
return memcmp(password, input, sizeof(password) - 1);
} int main(int argc, char **argv) { if (check(argv[1]) == 0) {
puts("You got it !!");
return EXIT_SUCCESS;
} puts("Wrong");
return EXIT_FAILURE; }

hook.c 内容,hook memcpy, 打印内容。

#include "arch/x86_64/syscall.c"
#define stdout 1 //gcc -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook int my_memcmp(const void* lhs, const void* rhs, int n) {
const char msg[] = "Hook add\n";
_write(stdout, msg, sizeof(msg));
_write(stdout, (const char*)lhs, n);
_write(stdout, "\n", 2);
_write(stdout, (const char*)rhs, n);
_write(stdout, "\n", 2);
return 0;
}

hook 脚本

import lief

crackme = lief.parse("crackme.bin")
hook = lief.parse("hook") segment_added = crackme.add(hook.segments[0]) my_memcmp = hook.get_symbol("my_memcmp")
my_memcmp_addr = segment_added.virtual_address + my_memcmp.value crackme.patch_pltgot('memcmp', my_memcmp_addr)
crackme.write("crackme.hooked")

参考:

https://lief.quarkslab.com/doc/tutorials/

https://github.com/lunixbochs/patchkit

可执行文件patch技术&&持续更新的更多相关文章

  1. 《WCF技术剖析》博文系列汇总[持续更新中]

    原文:<WCF技术剖析>博文系列汇总[持续更新中] 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖 ...

  2. 中国.NET:各地微软技术俱乐部汇总(持续更新中...)

    中国.NET:各地微软技术俱乐部汇总(持续更新中...)   本文是转载文,源地址: https://www.cnblogs.com/panchun/p/JLBList.html by ​史记微软. ...

  3. CNUTCon2017全球运维技术大会(持续更新中) - 斯达克学院 - 实战驱动的 IT 教育平台 - Powered By EduSoho

    CNUTCon2017全球运维技术大会(持续更新中) - 斯达克学院 - 实战驱动的 IT 教育平台 - Powered By EduSoho   https://new.stuq.org/cours ...

  4. (持续更新) C# 面试技术点、常见SQL技术点 和 解决高并发的相关技术

    这篇博客 持续更新. 方便小伙伴们学习与面试前的复习

  5. Linux面试题(史上最全、持续更新、吐血推荐)

    文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...

  6. 我的敏捷、需求分析、UML、软件设计电子书 - 下载(持续更新中)

    我将所有我的电子书汇总在一起,方便大家下载!(持续更新) 文档保存在我的网站——软件知识原创基地上(www.umlonline.org),请放心下载. 1)软件设计是怎样炼成的?(2014-4-1 发 ...

  7. java视频教程 Java自学视频整理(持续更新中...)

    视频教程,马士兵java视频教程,java视频 1.Java基础视频 <张孝祥JAVA视频教程>完整版[RMVB](东西网) 历经5年锤炼(史上最适合初学者入门的Java基础视频)(传智播 ...

  8. BLE资料应用笔记 -- 持续更新

    BLE资料应用笔记 -- 持续更新 BLE 应用笔记 小书匠 简而言之,蓝牙无处不在,易于使用,低耗能和低使用成本.'让我们'更深入地探索这些方面吧. 蓝牙无处不在-,您可以在几乎每一台电话.笔记本电 ...

  9. ( 译、持续更新 ) JavaScript 上分小技巧(三)

    最近家里杂事较多,自学时间实在少的可怜,所以都在空闲时间看看老外写的内容,学习之外顺便翻译分享~等学习的时间充足些再写写自己的一些学习内容和知识点分析(最近有在接触的:复习(C#,SQL).(学习)T ...

随机推荐

  1. CentOS7启动Tomcat报错:./startup.sh: Permission denied

    错误信息:./startup.sh: Permission denied 执行./startup.sh,或者./shutdown.sh的时候, 报:Permission denied,因为是执行tom ...

  2. (转)MySQL 加锁处理分析

    MySQL 加锁处理分析 原文:http://hedengcheng.com/?p=771 1    背景    1 1.1    MVCC:Snapshot Read vs Current Read ...

  3. syslog之一:Linux syslog日志系统详解

    目录: <syslog之一:Linux syslog日志系统详解> <syslog之二:syslog协议及rsyslog服务全解析> <syslog之三:建立Window ...

  4. Java之集合(十八)DelayQueue

    转载请注明源出处:http://www.cnblogs.com/lighten/p/7493735.html 1.前言 本章介绍阻塞队列DelayQueue,这是一个无界阻塞队列.其存储延时的元素,只 ...

  5. 基于TrueLicense实现产品License验证功能

    受朋友所托,需要给产品加上License验证功能,进行试用期授权,在试用期过后,产品不再可用. 通过研究调查,可以利用Truelicense开源框架实现,下面分享一下如何利用Truelicense实现 ...

  6. 运维甩锅神器---Jumpserver

    简介jumpserver 也就是跳板机,堡垒机,主要用于免密钥登陆web终端,可以对所有操作进行记录,录像!对所有服务器进行资产管理, 给开发人员分配登陆主机的权限和sudo权限,为运维人员省了很多手 ...

  7. 基于obs+nginx-rtmp-module搭建自己直播的系统

    前言 一句唠叨,工欲善其事,必先利其器,在程序员的工作里,搭建各种环境往往花费过多不必要的时间,这里建议搭建服务端环境时,尽量避开win.macos这种系统,个人比较推崇centos. 操作 下面以c ...

  8. Shell如何解决文件流管道的文本拼接失效问题

    前言: 近期由于业务的需要,需实现通过监控日志文件的内容并定时将日志的有效内容通过邮件进行告警. 文本内容的格式如下: 1 aaa 2 bbb 4 ccc 7 ddd 希望输出: bbb ccc 版本 ...

  9. txt文本框设为密码模式后,后台(服务器端)设置不了值

    txt文本框设为密码模式后,因为安全问题,后台(服务器端)设置不了值,只有在前台(客户端)复制才能显示

  10. Deep Q-Network 学习笔记(六)—— 改进④:dueling dqn

    这篇同样是完全没看懂 Orz,这里只做实现记录.. 要改动的地方只是在神经网络的最后一层做下调整即可. def create(self): neuro_layer_1 = 3 w_init = tf. ...