趁热打铁,再练习一下pin插桩。

在写Cellular的题解之前,先写一下利用pin爆破。

linux上爆破示例

编写如下示例程序:

#include <stdio.h>
#include <string.h>
int main(){
    char flag[] = "this is the true flag.";
    char input[32];

    //scanf("%s", input);
    scanf("%[^\n]", input);
    
    int len = strlen(flag);
    //printf("%d %d", len, strlen(input));
    if (len != strlen(input)){
        printf("wrong\n");
        return 0;
    }
    
    for (int i = 0; i < len; i++){
        if (flag[i] != input[i]){
            printf("wrong\n");
            return 0;    
        }    
    }
    printf("right\n");
    return 0;
}

该程序进行比较flag的时候,如果不正确,则立即返回错误。也就是说可以通过返回前的最后一次比较的下标i,来判断输入字符串的前多少个字符是对的。

上述程序在linux上编译后,main函数的地址是这样的:

image-20201118013912796

动调时有aslr。

所以需要关掉aslr:sudo echo 0 > /proc/sys/kernel/randomize_va_space

然后动调发现运行到0x5555555547DF时,rax存的就是下标i。

image-20201118014024636

所以在0x5555555547DF插桩,保存此时的rax,rax一直更新直至退出。

pintools脚本如下,iyzyi-mifeng.cpp:

/*
 * Copyright 2002-2020 Intel Corporation.
 * 
 * This software is provided to you as Sample Source Code as defined in the accompanying
 * End User License Agreement for the Intel(R) Software Development Products ("Agreement")
 * section 1.L.
 * 
 * This software and the related documents are provided as is, with no express or implied
 * warranties, other than those that are expressly stated in the License.
 */

#include <stdio.h>
#include <iostream>
#include "pin.H"

//FILE * trace;

static long int i = 0;       // 记录i的最终数值

VOID update_i(ADDRINT *ip,ADDRINT *rax) { 
    //printf("%p\t%p\n", ip, rax);
    if((long int)ip == 0x5555555547DF){
        i = (long int)rax;
    }
}

VOID Instruction(INS ins, VOID *v){
    long int opList[] = {0x5555555547DF};//需要插桩的地址
    long int ip = INS_Address(ins);
    bool flag = false;
    for (size_t i = 0; i < 1; i++){
        if(ip == opList[i]){
            flag = true;
            break;
        }
    }//进行指令级别插桩
    if(flag){//IPOINT_BEFORE在指令执行前插桩
        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)update_i,
                   IARG_INST_PTR,
                   IARG_REG_VALUE,REG_EAX,
                   IARG_END);
    }
  //以IARG_REG_VALUE,REG_EAX的形式将执行这条指令前的寄存器值传入
}

VOID Fini(INT32 code, VOID *v)
{
    //fprintf(trace, "#eof\n");
    //fclose(trace);
    printf("<pin>%ld</pin>\n", i);
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    PIN_ERROR("This Pintool log flow\n" 
              + KNOB_BASE::StringKnobSummary() + "\n");
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    //trace = fopen("itrace.out", "w");

    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();

    // Register Instruction to be called to instrument instructions
    INS_AddInstrumentFunction(Instruction, 0);
    // Register Fini to be called when the application exits
    PIN_AddFiniFunction(Fini, 0);

    // Start the program, never returns
    PIN_StartProgram();

    return 0;
}

测试几个数据:

iyzyi@ubuntu:~/re/pin/pin-3.16-98275-ge0db48c31-gcc-linux/source/tools/ManualExamples$ ../../../pin -t obj-intel64/iyiyzyi@ubuntu:~/re/pin/pin-3.16-98275-ge0db48c31-gcc-linux/source/tools/ManualExamples$ ../../../pin -t obj-intel64/iyzyi-mifeng.so -- /home/iyzyi/re/testpin
this******************
wrong
<pin>4</pin>

iyzyi@ubuntu:~/re/pin/pin-3.16-98275-ge0db48c31-gcc-linux/source/tools/ManualExamples$ ../../../pin -t obj-intel64/iyzyi-mifeng.so -- /home/iyzyi/re/testpin
this is the true******
wrong
<pin>16</pin>

iyzyi@ubuntu:~/re/pin/pin-3.16-98275-ge0db48c31-gcc-linux/source/tools/ManualExamples$ ../../../pin -t obj-intel64/iyzyi-mifeng.so -- /home/iyzyi/re/testpin
this is the true flag.
right
<pin>21</pin>

iyzyi@ubuntu:~/re/pin/pin-3.16-98275-ge0db48c31-gcc-linux/source/tools/ManualExamples$ ../../../pin -t obj-intel64/iyzyi-mifeng.so -- /home/iyzyi/re/testpin
this is th            
wrong
<pin>0</pin>

image-20201118013205421

所以每次运行,我们都可以知道输入的字符串的前多少个字符是正确的。

写出如下的python自动化爆破脚本:

from subprocess import Popen, PIPE
import string, struct, re
from sys import argv, exit

def list2bytes(l):
    b = b''
    for i in range(len(l)):
        b += struct.pack('B', l[i])
    return b


pinPath = "/home/iyzyi/re/pin/pin-3.16-98275-ge0db48c31-gcc-linux/pin"
soPath = "/home/iyzyi/re/pin/pin-3.16-98275-ge0db48c31-gcc-linux/source/tools/ManualExamples/obj-intel64/iyzyi-mifeng.so"
challengePath = "/home/iyzyi/re/testpin"

def run(input):
    pin = Popen([pinPath, '-t', soPath, '--', challengePath], stdin = PIPE, stdout = PIPE)
    pin.stdin.write(input + b'\n')
    out, err = pin.communicate()
    return out


if __name__ == "__main__":
    flag = []
    for i in range(22):
        for ch in range(32, 127):

            input = list2bytes(flag) + struct.pack('B', ch) + b'*' * (22 - len(flag) - 1)
            print(input)

            output = run(input).decode()
            print(output)

            n = int(re.search(r'<pin>(\d+?)</pin>', output).group(1))
            if n > i:
                flag.append(ch)
                print(list2bytes(flag))
                break

            if 'right' in output:
                print(list2bytes(flag))
                exit()

image-20201118013512925

image-20201118013402069

windows上爆破示例

还是上面那个程序。

脚本也还是上面的,只需要改一下对应的地址0x5555555547DF改成0x40158F(64位的)或者0x401625 (32位的),然后vs编译一下。就可以啦。

在 Windows 下,可以使用 "/{Pin根目录}/source/tools/MyPinTool" 下配置好的 Visual Studio 项目进行开发 (VS真香)。

编译pintools脚本的话,一定要注意和题目程序的位数一致。

pin, 脚本dll, 题目程序这三者的位数均需一致:

image-20201118022950521

python爆破脚本的话,改改路径就能继续跑。

Cellular

这题也是可以利用cmp的次数来侧面获取当前正确字符数。不过这题分支情况太多了,要跑一个dfs。

插桩位置:

image-20201118035440243

image-20201118035414315

插桩脚本:

/*
* Copyright 2002-2020 Intel Corporation.
*
* This software is provided to you as Sample Source Code as defined in the accompanying
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
* section 1.L.
*
* This software and the related documents are provided as is, with no express or implied
* warranties, other than those that are expressly stated in the License.
*/

#include <stdio.h>
#include <iostream>
#include "pin.H"

//FILE * trace;

static long int i = 0;       // 记录i的最终数值

VOID update_i(ADDRINT *ip, ADDRINT *eax) {
    //printf("%p\t%p\n", ip, rax);
    if ((long int)ip == 0x4021B1) {
        i = *(eax);
    }
}

VOID Instruction(INS ins, VOID *v) {
    long int opList[] = { 0x4021B1 };//需要插桩的地址
    long int ip = INS_Address(ins);
    bool flag = false;
    for (size_t i = 0; i < 1; i++) {
        if (ip == opList[i]) {
            flag = true;
            break;
        }
    }//进行指令级别插桩
    if (flag) {//IPOINT_BEFORE在指令执行前插桩
        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)update_i,
            IARG_INST_PTR,
            IARG_REG_VALUE, REG_EAX,
            IARG_END);
    }
    //以IARG_REG_VALUE,REG_EAX的形式将执行这条指令前的寄存器值传入
}

VOID Fini(INT32 code, VOID *v)
{
    //fprintf(trace, "#eof\n");
    //fclose(trace);
    printf("<pin>%ld</pin>\n", i);
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    PIN_ERROR("This Pintool log flow\n"
        + KNOB_BASE::StringKnobSummary() + "\n");
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    //trace = fopen("itrace.out", "w");

    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();

    // Register Instruction to be called to instrument instructions
    INS_AddInstrumentFunction(Instruction, 0);
    // Register Fini to be called when the application exits
    PIN_AddFiniFunction(Fini, 0);

    // Start the program, never returns
    PIN_StartProgram();

    return 0;
}

爆破脚本:

from subprocess import Popen, PIPE
import string, struct, re
from sys import argv, exit

def list2bytes(l):
    b = b''
    for i in range(len(l)):
        b += struct.pack('B', l[i])
    return b


pinPath = r"D:\tools\pin\ia32\bin\pin.exe"
soPath = r"D:\tools\pin\source\tools\MyPinTool\Release\MyPinTool.dll"
challengePath = r"D:\tools\Cellular.exe"

def run(input):
    pin = Popen([pinPath, '-t', soPath, '--', challengePath], stdin = PIPE, stdout = PIPE)
    pin.stdin.write(input + b'\n')
    out, err = pin.communicate()
    return out

flag = []

def dfs(deep):
    global flag

    if deep == 25:
        return

    for ch in [ord('L'), ord('R')]:
        input = list2bytes(flag) + struct.pack('B', ch) + b'L' * (25 - len(flag) - 1)
        print('input:\t', input)

        output = run(input)#.decode()
        print('output:\t', output)

        try:
            n = int(re.search(rb'<pin>(\d+?)</pin>', output).group(1))
            if n >= deep:
                flag.append(ch)
                print('flag:\t', list2bytes(flag))
                print()

                dfs(deep+1)
                flag = flag[:-1]
        except Exception as e:
            return

        if b'Congrulations' in output:
            exit()

if __name__ == "__main__":
    dfs(0)

爆破结果:

image-20201118035324027

image-20201118035300213

flag是md5(RLRLLRLLLRRLLRLLRLRLLRLLR)

参考

Last modification:December 1st, 2020 at 08:18 pm