学破解第193天,《攻防世界reverse练习区re1-100》学习
前言:  坛友们,年轻就是资本,和我一起逆天改命吧,我的学习过程全部记录及学习资源:(https://www.52pojie.cn/thread-1582287-1-1.html)
**立帖为证!--------记录学习的点点滴滴**
## 0x1 收集信息
  1.首先,还是查壳,然后看一看文件格式,是windows平台还是linux平台的,是32位的,还是64位的。
!(https://s1.ax1x.com/2022/04/18/LdvZAU.png)
  2.将文件复制到kali中,然后将ida目录下的linux_server64也复制过来,防止一会远程调试,先备用。
!(https://s1.ax1x.com/2022/04/18/LdxeqP.png)
  3.运行一下程序,提示输入key,随便输入后提示wrong。
!(https://s1.ax1x.com/2022/04/18/LdxhJe.png)
  4.现在小菜鸟已经收集到一下信息了:
1)目标是64位linux程序,没有加壳
2)程序流程是输入key进行校验
3)我的目的是找到正确的key
## 0x2 静态分析
  1.将程序拖进ida,找到main函数,F5可以看到伪代码:
```
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__pid_t v3; // eax
size_t v4; // rax
ssize_t v5; // rbx
bool v6; // al
bool bCheckPtrace; //
ssize_t numRead; //
ssize_t numReada; //
char bufWrite; // BYREF
char bufParentRead; // BYREF
unsigned __int64 v12; //
v12 = __readfsqword(0x28u);
bCheckPtrace = detectDebugging();
if ( pipe(pParentWrite) == -1 )
exit(1);
if ( pipe(pParentRead) == -1 )
exit(1);
v3 = fork();
if ( v3 != -1 )
{
if ( v3 )
{
close(pParentWrite);
close(pParentRead);
while ( 1 )
{
printf("Input key : ");
memset(bufWrite, 0, sizeof(bufWrite));
gets(bufWrite);
v4 = strlen(bufWrite);
v5 = write(pParentWrite, bufWrite, v4);
if ( v5 != strlen(bufWrite) )
printf("parent - partial/failed write");
do
{
memset(bufParentRead, 0, sizeof(bufParentRead));
numReada = read(pParentRead, bufParentRead, 0xC8uLL);
v6 = bCheckPtrace || checkDebuggerProcessRunning();
if ( !v6 && checkStringIsNumber(bufParentRead) && atoi(bufParentRead) )
{
puts("True");
if ( close(pParentWrite) == -1 )
exit(1);
exit(0);
}
puts("Wrong !!!\n");
}
while ( numReada == -1 );
}
}
close(pParentWrite);
close(pParentRead);
while ( 1 )
{
memset(bufParentRead, 0, sizeof(bufParentRead));
numRead = read(pParentWrite, bufParentRead, 0xC8uLL);
if ( numRead == -1 )
break;
if ( numRead )
{
if ( !childCheckDebugResult()
&& bufParentRead == 123
&& strlen(bufParentRead) == 42
&& !strncmp(&bufParentRead, "53fc275d81", 0xAuLL)
&& bufParentRead == 125
&& !strncmp(&bufParentRead, "4938ae4efd", 0xAuLL)
&& confuseKey(bufParentRead, 42)
&& !strncmp(bufParentRead, "{daf29f59034938ae4efd53fc275d81053ed5be8c}", 0x2AuLL) )
{
responseTrue();
}
else
{
responseFalse();
}
}
}
exit(1);
}
exit(1);
}
```
  2.看上去代码很多,先不慌,分段拆解,看这里面居然还有反调试checkDebuggerProcessRunning,找到wrong,看这里,似乎有点晕,提示wrong就循环,提示true就直接退出,那就说明这个if不该进去,直接把这段代码pass掉。
```
if ( !v6 && checkStringIsNumber(bufParentRead) && atoi(bufParentRead) )
{
puts("True");
if ( close(pParentWrite) == -1 )
exit(1);
exit(0);
}
puts("Wrong !!!\n");
```
  3.write和read是读文件和写文件,这里pParentWrite存储着我输入的字符串,pParentRead读取字符串。
```
printf("Input key : ");
memset(bufWrite, 0, sizeof(bufWrite));
gets(bufWrite);
v4 = strlen(bufWrite);
v5 = write(pParentWrite, bufWrite, v4);
memset(&s, 0, 0xC8uLL);
v9 = read(pParentRead, &s, 0xC8uLL);
```
  4.继续往后翻,这里将pParentWrite里面的内容复制给s。
```
memset(&s, 0, 0xC8uLL);
v8 = read(pParentWrite, &s, 0xC8uLL);
```
  5.继续,看到关键的比较了,学数组的时候小菜鸟知道数组名就是数组首地址,这里先判断第一个字符是不是{,接着判断长度是不是42,然后晕圈,v12和v13咋来的?
```
if ( !(unsigned __int8)childCheckDebugResult()
&& s == 123
&& strlen(&s) == 42
&& !strncmp(v12, "53fc275d81", 0xAuLL)
&& *(&s + strlen(&s) - 1) == 125
&& !strncmp(v13, "4938ae4efd", 0xAuLL)
&& (unsigned __int8)confuseKey(&s, 42) == 1
&& !strncmp(&s, "{daf29f59034938ae4efd53fc275d81053ed5be8c}", 0x2AuLL) )
{
responseTrue();
}
else
{
responseFalse();
}
```
  6.去反汇编窗口,按空格键,找到提示字符串,看到如下图所示信息,怀疑我的IDA F5反编译不准确,硬着头皮啃汇编吧,流程图中的汇编上面都是rbp+bufParentRead(我输入的字符串)进行比较,还是没看懂,重启IDA,出现一个提示框全勾上再说。
!(https://s1.ax1x.com/2022/04/18/LwV7OU.png)
```
if ( !childCheckDebugResult()
&& bufParentRead == 123
&& strlen(bufParentRead) == 42
&& !strncmp(&bufParentRead, "53fc275d81", 0xAuLL)
&& bufParentRead == 125
&& !strncmp(&bufParentRead, "4938ae4efd", 0xAuLL)
&& confuseKey(bufParentRead, 42)
&& !strncmp(bufParentRead, "{daf29f59034938ae4efd53fc275d81053ed5be8c}", 0x2AuLL) )
```
  7.这下子逻辑清晰了,输入字符串第一个字符必须是{,倒数第一个字符必须是},然后下标1-10对应的字符必须是"53fc275d81",下标31-40对应的字符必须是4938ae4efd,接着调用confuseKey函数将字符串变换,最后和{daf29f59034938ae4efd53fc275d81053ed5be8c}进行比较,看看confuseKey函数:
```
bool __cdecl confuseKey(char *szKey, int iKeyLength)
{
char szPart1; // BYREF
char szPart2; // BYREF
char szPart3; // BYREF
char szPart4; // BYREF
unsigned __int64 v7; //
v7 = __readfsqword(0x28u);
*(_QWORD *)szPart1 = 0LL;
*(_DWORD *)&szPart1 = 0;
*(_WORD *)&szPart1 = 0;
szPart1 = 0;
*(_QWORD *)szPart2 = 0LL;
*(_DWORD *)&szPart2 = 0;
*(_WORD *)&szPart2 = 0;
szPart2 = 0;
*(_QWORD *)szPart3 = 0LL;
*(_DWORD *)&szPart3 = 0;
*(_WORD *)&szPart3 = 0;
szPart3 = 0;
*(_QWORD *)szPart4 = 0LL;
*(_DWORD *)&szPart4 = 0;
*(_WORD *)&szPart4 = 0;
szPart4 = 0;
if ( iKeyLength != 42 )
return 0;
if ( !szKey )
return 0;
if ( strlen(szKey) != 42 )
return 0;
if ( *szKey != 123 )
return 0;
strncpy(szPart1, szKey + 1, 0xAuLL);
strncpy(szPart2, szKey + 11, 0xAuLL);
strncpy(szPart3, szKey + 21, 0xAuLL);
strncpy(szPart4, szKey + 31, 0xAuLL);
memset(szKey, 0, 0x2AuLL);
*szKey = 123;
strcat(szKey, szPart3);
strcat(szKey, szPart4);
strcat(szKey, szPart1);
strcat(szKey, szPart2);
szKey = 125;
return 1;
}
```
  8.前面都没啥,主要看strncpy赋值语句和strcat字符串连接语句,可以看到把字符串分为4段,每段长度位10,然后按照3412的顺序进行连接,先看看替换后的字符串:
```
daf29f5903 第3段
4938ae4efd 第4段
53fc275d81 第1段
053ed5be8c 第2段
```
  9.调整顺序为1234得到key,{53fc275d81053ed5be8cdaf29f59034938ae4efd},运行程序验证一下,成功。
!(https://s1.ax1x.com/2022/04/18/LwnhHU.png)
## 0x3 总结
  1.去攻防世界提交:flag{53fc275d81053ed5be8cdaf29f59034938ae4efd},居然提示错了,然后试{53fc275d81053ed5be8cdaf29f59034938ae4efd}还是错了,最后输入53fc275d81053ed5be8cdaf29f59034938ae4efd终于对了。
  2.这道题的逻辑其实很简单,主要是前面看伪代码懵圈了,然后想着IDA动态调试看参数的时候有点晕,一直断不下来,不知道是不是那个反调试的原因,真是奇了怪了。 风鸣雨喧 发表于 2022-4-19 17:50
学破解第164天,楼主学习记录导航贴更新!
楼主,这个帖子是不是该更新了下啊,你最新的都没有,只到187 ...
这个帖子在哪? 风鸣雨喧 发表于 2022-4-24 22:39
点我前面发的那个文字(蓝色的)就直接跳转过去了,那是一个链接
感谢,非常感谢,最近想试试破解 能帮帮我吗?
非常不错。。。。。。。 感谢分享 感谢分享,学习技术。:lol 大佬,厉害 感谢分享 感谢分享,认真学习 感谢分享 学破解第164天,楼主学习记录导航贴更新!
楼主,这个帖子是不是该更新了下啊,你最新的都没有,只到187天,后面的呢?
https://s3.bmp.ovh/imgs/2022/04/19/92d3c77305747ba0.png
页:
[1]
2