狗儿

热爱的话就坚持吧~

0%

西湖论剑逆向Cellular

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

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

linux上爆破示例

编写如下示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/*
* 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;
}

测试几个数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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自动化爆破脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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

插桩脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/*
* 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;
}

爆破脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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)

参考