printf打印格式的谜题

在做逆向作业的时候遇到一个问题:把从内存读回的数据用

char a[1000]
LOOP
    printf("%x",a);

的方式输出到屏幕,再转储成二进制的话,总会出现数据丢失的情况。由于输出文件是一个MP3文件,一旦有数据丢失就意味着文件结构破坏,无法解析。
后来发现使用

LOOP
    printf("%c",a);

的方式打印,直接就可以得到完好的二进制文件,./decrypt2 > file.mp3的结果甚至可以直接播放。于是研究了一下使用printf格式化为%x和%c的差异。
用例文件

#include <stdio.h>

int main()
{
    int a = 0x99;
    unsigned char b[4] = {0x4,0x0,0x0,0x1};
    printf("int a = %x\n", a);
    for(int i = 0; i < 4; i++)
        printf("%x", b[i]);
    for(int i = 0; i < 4; i++)
        printf("%c", b[i]);
    return 0;
}

用%x输出的结果是4001,而用%c输出的结果是04000001。由于%x是按照int类型输出,因此并不会理会数据原本的宽度,直接打印。因此如果使用%x格式,则一定要记好原先的宽度,使用诸如%02x的方式输出。
当然,%c输出的是直接的binary,也就是ASCII字符,屏幕打印出来看着像乱码,在开头提到的环境中一步到位。如果是需要得到数字值,则必须使用%x辅以数值宽度的方式。


LD_PRELOAD的作业非常有趣,是将一个二进制文件中的一段运行时数据偷出来。解密函数用静态编译,无缘动手,居然最终的破题是在程序最后不起眼的free函数。通过preload自己写的free动态库,覆盖libc的原函数,使得在源程序在调用free函数的时候自动将数据段打印出来。

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
//#include <evp.h>
/*
typedef int (*orig_decryptupdate_type)(void *ctx, unsigned char *out, int *outl, \
        const unsigned char *in, int inl);

int EVP_DecryptUpdate(void *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl)
{
    printf("I'm in!\n");
    
    orig_decryptupdate_type orig_DecryptUpdate;
    orig_DecryptUpdate = (orig_decryptupdate_type)dlsym(RTLD_NEXT, "EVP_DecryptUpdate");

    printf("input: %s\n", in);
    printf("output: %s\n\n", out);

    return orig_DecryptUpdate(ctx, out, outl, in, inl);
}
*/

typedef void (*orig_free_type)(void *ptr);

void free(void *ptr)
{
    long len = 0x98CA0-0x6050;
    unsigned char out[0x98ca0-0x6050];
    unsigned char *p = ptr;
    
    orig_free_type orig_free;
    orig_free = (orig_free_type)dlsym(RTLD_NEXT, "free");
    
    //printf("\n+++ I'm in `free`! +++\n");
    
    if(*p == 0x49) {
        memcpy(out, (unsigned char*)p, len);
        //printf("\n%ld\n",strlen(out));
        for(long i = 0; i < len; i++)
        {   
            printf("%x",(unsigned char)out[i]);
        }
    }
    
    orig_free(ptr);
}

编译和设置

$ gcc -shared -fPIC decrypt-dlib.c -o decrypt-dlib.so -ldl
$ export LD_PRELOAD="$PWD/decrypt-dlib.so"

运行

$ ./decrypt2 > free_bin.mp3

播放即可。

Comments
Write a Comment