新手入门的第一次爆破——010Editor
程序员文章站
2022-07-05 23:14:15
大约一个月以前,准备认真踏实学习加密解密。在这段时间里,汇编语言、看雪经典《加密与解密(第三版)》、一系列相关工具及文档等等弄得我是晕头转向,但是我坚信持之以恒,坚持到底,我一定能突破困难,突...
大约一个月以前,准备认真踏实学习加密解密。在这段时间里,汇编语言、看雪经典《加密与解密(第三版)》、一系列相关工具及文档等等弄得我是晕头转向,但是我坚信持之以恒,坚持到底,我一定能突破困难,突破自己!希望能成功的成为一名Reserver-er.
本文是我的第一个爆破程序,菜鸟水平,但对我是很大的鼓励了,还请大牛们不要见笑。
很早之前,下载了一个010Editor,但是基本就没用(到目前为止,下载到电脑里的很多反编译,逆向等很多工具还没动。。。辩解一下呀:不是我懒,一是软件实在是太多,而且很多都不知道是干什么的;二是一直在学习IDA和olldbg和Stud_PE等,这几个已经让我感觉是 博大精深 了!只能说,后面的路还有很长,加油!!!)今天为了找一个功能,运行了010Editor,但是发现他的试用期已经到期了。。。正好,发现是要输入用户名和注册码的,心里顿时一阵窃喜~O(∩_∩)O哈哈~,学了这么久的破解终于有用武之地了!想到这,我就想起了那句哲人的名言:“当你手里拿着锤子的时候,整个世界都成了钉子”!O(∩_∩)O哈哈~,太精辟了!
好了,闲话说得太多,别喷我哦。。。言归正传,开工!
首先运行软件,弹出输入注册码的页面,随便填入如下:
很遗憾,弹出错误:
不过没关系,关闭,用olldbg重新打开,字符串查找“Invalid password”,很快就找到如下:
引用:
超级字串参考+ , 条目1019
地址=0046CF76
反汇编=MOV EDX,010Edito.005C9733
文本字串=invalid password. please enter your name and password exactly as given when registering.
双击进入,发现不仅仅这一句,前后还有很多关于比如password XXX的信息,不过我最感兴趣的是这一句:
引用:
password accepted. thank you for purchasing 010 editor!
将局部这段代码显示如下:
引用:
0046CE5A |. 6A 01 PUSH 1 ; /Arg2 = 00000001
0046CE5C |. FF35 A8375A00 PUSH DWORD PTR DS:[5A37A8] ; |该地址指向输入的用户名和密码,下面函数要跟入
0046CE62 |. E8 25E3FFFF CALL 010Edito.0046B18C ; \010Edito.0046B18C
0046CE67 |. 83C4 08 ADD ESP,8
0046CE6A |. 8945 90 MOV DWORD PTR SS:[EBP-70],EAX
0046CE6D |. 817D 90 DB000>CMP DWORD PTR SS:[EBP-70],0DB
0046CE74 75 35 JNZ SHORT 010Edito.0046CEAB
0046CE76 |. 66:C745 B0 50>MOV WORD PTR SS:[EBP-50],50
0046CE7C |. BA F9955C00 MOV EDX,010Edito.005C95F9 ; password accepted. thank you for purchasing 010 editor!
可以看出,只有010Edito.0046B18C的函数返回值为0xDB,也就是219时,用户名密码才是通过的。用IDA加载010Editor,还原该函数,代码如下:
代码:
signed int __cdecl sub_46B18C(int a1, unsigned int a2)
{
int v2; // eax@1
int v3; // eax@2
signed int result; // eax@5
int v5; // eax@8
int v6; // eax@9
v2 = sub_46B200(a1, a2) - 45;
if ( v2 )
{
v3 = v2 - 33;
if ( v3 )
{
if ( v3 == 153 )
{
result = 375;
}
else
{
v5 = sub_46B4D0(a1) - 23;
if ( v5 )
{
v6 = v5 - 19;
if ( v6 )
{
if ( v6 == 270 )
result = 47;
else
result = 375;
}
else
{
result = 375;
}
}
else
{
result = 113;
}
}
}
else
{
result = 16;
}
}
else
{
result = 219;
}
return result;
}
很明显看出,还是返回219的条件是v2 = sub_46B200(a1, a2) - 45要等于0,也就是sub_46B200需要返回值为45(0x2d)。双击sub_46B200进入该函数,代码如下:
代码: www.2cto.com
signed int __cdecl sub_46B200(int address, unsigned int a2)
{
signed int result; // eax@3
int v3; // eax@5
char v4; // ST0C_1@5
__int16 v5; // ST10_2@9
int v6; // ST0C_4@16
int v7; // ST08_4@16
int v8; // ST04_4@16
const char *v9; // eax@16
unsigned __int8 v10; // [sp+4h] [bp-3Ch]@4
unsigned __int8 v11; // [sp+5h] [bp-3Bh]@9
unsigned __int8 v12; // [sp+6h] [bp-3Ah]@9
char v13; // [sp+7h] [bp-39h]@8
char v14; // [sp+8h] [bp-38h]@16
char v15; // [sp+9h] [bp-37h]@9
char v16; // [sp+Ah] [bp-36h]@9
char v17; // [sp+Bh] [bp-35h]@9
int v18; // [sp+Ch] [bp-34h]@29
unsigned int v19; // [sp+10h] [bp-30h]@16
int v20; // [sp+14h] [bp-2Ch]@4
__int16 v21; // [sp+28h] [bp-18h]@5
int v22; // [sp+34h] [bp-Ch]@5
char v23; // [sp+3Ch] [bp-4h]@5
__InitExceptBlockLDTC();
if ( (unsigned __int8)System::AnsiString::IsEmpty(address + 4)
|| (unsigned __int8)System::AnsiString::IsEmpty(address + 8) )
return 147;
sub_46BCD8(address, &v10);
v20 = 0;
do
{
v21 = 8;
sub_5A0BA4(&v23, *(&off_5C8D50 + v20));
++v22;
v3 = System::AnsiString::operator__(address + 4, &v23);
--v22;
System::AnsiString::_AnsiString(v3);
if ( v4 )
return 231;
++v20;
}
while ( v20 < 1 );
if ( v13 == -100 )
{
v5 = (unsigned __int8)(v15 ^ v12) + ((unsigned __int8)(v17 ^ v11) << 8);
*(_DWORD *)(address + 24) = (unsigned __int8)sub_46C612(v16 ^ v10);
*(_DWORD *)(address + 28) = (unsigned __int16)sub_46C628(v5);
if ( !*(_DWORD *)(address + 24) || !*(_DWORD *)(address + 28) || *(_DWORD *)(address + 28) > 0x3E8u )
return 231;
}
else
{
if ( v13 != -4 )
return 231;
*(_DWORD *)(address + 24) = 255;
*(_DWORD *)(address + 28) = 1;
}
v6 = *(_DWORD *)(address + 28);
v7 = *(_DWORD *)(address + 24);
v8 = v13 == -100;
v9 = (const char *)System::AnsiString::c_str(address + 4);
v19 = sub_46C370(v9, v8, v7, v6);
if ( (_BYTE)v19 == v14 )
{
if ( BYTE1(v19) == v15 )
{
if ( (unsigned __int8)(v19 >> 16) == v16 )
{
if ( BYTE3(v19) == v17 )
{
if ( v13 == -100 )
{
if ( a2 <= *(_DWORD *)(address + 24) )
result = 45; // ==45 0x2d时才正确
else
result = 78;
}
else
{
if ( v13 == -4 )
{
v18 = (v12 << 16) + (v11 << 8) + v10;
v18 = sub_46C5CD((v12 << 16) + (v11 << 8) + v10, v19);
if ( v18 )
{
*(_DWORD *)(address + 20) = v18;
result = 147;
}
else
{
result = 231;
}
}
else
{
result = 231;
}
}
}
else
{
result = 231;
}
}
else
{
result = 231;
}
}
else
{
result = 231;
}
}
else
{
result = 231;
}
return result;
}
可以看到在我注释的地方,就是返回45的情况。
正确的挑战指令在olldbg中如下:
引用:
0046B447 > \B8 2D000000 MOV EAX,2D
0046B44C . 8B55 D8 MOV EDX,DWORD PTR SS:[EBP-28]
0046B44F . 64:8915 00000>MOV DWORD PTR FS:[0],EDX
0046B456 . EB 73 JMP SHORT 010Edito.0046B4CB
所以,爆破就很简单了。直接在验证用户名,密码存在并且密码位数正确的地方通过后的指令改为到此的跳转:
即一些这句
引用:
0046B2B6 /0F85 83000000 JNZ 010Edito.0046B33F
,改成:
引用:
0046B2B6 . /E9 8C010000 JMP 010Edito.0046B447
0046B2BB |90 NOP
将修改写到可执行文件,就大功告成了!
事实上,我花了更多的时间希望研究出他的验证函数,IDA还原如下:
代码:
int __cdecl sub_46C370(const char *s, int a2, char a3, char a4)
{
int v4; // eax@2
size_t v6; // [sp+4h] [bp-10h]@1
signed int i; // [sp+8h] [bp-Ch]@1
int v8; // [sp+10h] [bp-4h]@1
v8 = 0;
v6 = strlen(s);
for ( i = 0; i < (signed int)v6; ++i )
{
v4 = toupper(s[i]);
if ( a2 )
v8 = word_5C91A8[(unsigned __int8)(15 * a4 + 13 * i)]
+ word_5C91A8[(unsigned __int8)(17 * a3 + 9 * i)]
+ word_5C91A8[(unsigned __int8)(19 * i)]
+ word_5C91A8[(unsigned __int8)(v4 + 47)] * (word_5C91A8[(unsigned __int8)(v4 + 13)] ^ (v8 + word_5C91A8[v4]));
else
v8 = word_5C91A8[(unsigned __int8)(15 * a4 + 13 * i)]
+ word_5C91A8[(unsigned __int8)(17 * a3 + 9 * i)]
+ word_5C91A8[(unsigned __int8)(7 * i)]
+ word_5C91A8[(unsigned __int8)(v4 + 23)] * (word_5C91A8[(unsigned __int8)(v4 + 63)] ^ (v8 + word_5C91A8[v4]));
}
return v8;
}
然后返回值需要满足上面sub_46B200中的一系列条件,不过很遗憾,分析还感觉有些困难,等以后功底更深了再分析算法吧~
本文是我的第一个爆破程序,菜鸟水平,但对我是很大的鼓励了,还请大牛们不要见笑。
很早之前,下载了一个010Editor,但是基本就没用(到目前为止,下载到电脑里的很多反编译,逆向等很多工具还没动。。。辩解一下呀:不是我懒,一是软件实在是太多,而且很多都不知道是干什么的;二是一直在学习IDA和olldbg和Stud_PE等,这几个已经让我感觉是 博大精深 了!只能说,后面的路还有很长,加油!!!)今天为了找一个功能,运行了010Editor,但是发现他的试用期已经到期了。。。正好,发现是要输入用户名和注册码的,心里顿时一阵窃喜~O(∩_∩)O哈哈~,学了这么久的破解终于有用武之地了!想到这,我就想起了那句哲人的名言:“当你手里拿着锤子的时候,整个世界都成了钉子”!O(∩_∩)O哈哈~,太精辟了!
好了,闲话说得太多,别喷我哦。。。言归正传,开工!
首先运行软件,弹出输入注册码的页面,随便填入如下:
很遗憾,弹出错误:
不过没关系,关闭,用olldbg重新打开,字符串查找“Invalid password”,很快就找到如下:
引用:
超级字串参考+ , 条目1019
地址=0046CF76
反汇编=MOV EDX,010Edito.005C9733
文本字串=invalid password. please enter your name and password exactly as given when registering.
双击进入,发现不仅仅这一句,前后还有很多关于比如password XXX的信息,不过我最感兴趣的是这一句:
引用:
password accepted. thank you for purchasing 010 editor!
将局部这段代码显示如下:
引用:
0046CE5A |. 6A 01 PUSH 1 ; /Arg2 = 00000001
0046CE5C |. FF35 A8375A00 PUSH DWORD PTR DS:[5A37A8] ; |该地址指向输入的用户名和密码,下面函数要跟入
0046CE62 |. E8 25E3FFFF CALL 010Edito.0046B18C ; \010Edito.0046B18C
0046CE67 |. 83C4 08 ADD ESP,8
0046CE6A |. 8945 90 MOV DWORD PTR SS:[EBP-70],EAX
0046CE6D |. 817D 90 DB000>CMP DWORD PTR SS:[EBP-70],0DB
0046CE74 75 35 JNZ SHORT 010Edito.0046CEAB
0046CE76 |. 66:C745 B0 50>MOV WORD PTR SS:[EBP-50],50
0046CE7C |. BA F9955C00 MOV EDX,010Edito.005C95F9 ; password accepted. thank you for purchasing 010 editor!
可以看出,只有010Edito.0046B18C的函数返回值为0xDB,也就是219时,用户名密码才是通过的。用IDA加载010Editor,还原该函数,代码如下:
代码:
signed int __cdecl sub_46B18C(int a1, unsigned int a2)
{
int v2; // eax@1
int v3; // eax@2
signed int result; // eax@5
int v5; // eax@8
int v6; // eax@9
v2 = sub_46B200(a1, a2) - 45;
if ( v2 )
{
v3 = v2 - 33;
if ( v3 )
{
if ( v3 == 153 )
{
result = 375;
}
else
{
v5 = sub_46B4D0(a1) - 23;
if ( v5 )
{
v6 = v5 - 19;
if ( v6 )
{
if ( v6 == 270 )
result = 47;
else
result = 375;
}
else
{
result = 375;
}
}
else
{
result = 113;
}
}
}
else
{
result = 16;
}
}
else
{
result = 219;
}
return result;
}
很明显看出,还是返回219的条件是v2 = sub_46B200(a1, a2) - 45要等于0,也就是sub_46B200需要返回值为45(0x2d)。双击sub_46B200进入该函数,代码如下:
代码: www.2cto.com
signed int __cdecl sub_46B200(int address, unsigned int a2)
{
signed int result; // eax@3
int v3; // eax@5
char v4; // ST0C_1@5
__int16 v5; // ST10_2@9
int v6; // ST0C_4@16
int v7; // ST08_4@16
int v8; // ST04_4@16
const char *v9; // eax@16
unsigned __int8 v10; // [sp+4h] [bp-3Ch]@4
unsigned __int8 v11; // [sp+5h] [bp-3Bh]@9
unsigned __int8 v12; // [sp+6h] [bp-3Ah]@9
char v13; // [sp+7h] [bp-39h]@8
char v14; // [sp+8h] [bp-38h]@16
char v15; // [sp+9h] [bp-37h]@9
char v16; // [sp+Ah] [bp-36h]@9
char v17; // [sp+Bh] [bp-35h]@9
int v18; // [sp+Ch] [bp-34h]@29
unsigned int v19; // [sp+10h] [bp-30h]@16
int v20; // [sp+14h] [bp-2Ch]@4
__int16 v21; // [sp+28h] [bp-18h]@5
int v22; // [sp+34h] [bp-Ch]@5
char v23; // [sp+3Ch] [bp-4h]@5
__InitExceptBlockLDTC();
if ( (unsigned __int8)System::AnsiString::IsEmpty(address + 4)
|| (unsigned __int8)System::AnsiString::IsEmpty(address + 8) )
return 147;
sub_46BCD8(address, &v10);
v20 = 0;
do
{
v21 = 8;
sub_5A0BA4(&v23, *(&off_5C8D50 + v20));
++v22;
v3 = System::AnsiString::operator__(address + 4, &v23);
--v22;
System::AnsiString::_AnsiString(v3);
if ( v4 )
return 231;
++v20;
}
while ( v20 < 1 );
if ( v13 == -100 )
{
v5 = (unsigned __int8)(v15 ^ v12) + ((unsigned __int8)(v17 ^ v11) << 8);
*(_DWORD *)(address + 24) = (unsigned __int8)sub_46C612(v16 ^ v10);
*(_DWORD *)(address + 28) = (unsigned __int16)sub_46C628(v5);
if ( !*(_DWORD *)(address + 24) || !*(_DWORD *)(address + 28) || *(_DWORD *)(address + 28) > 0x3E8u )
return 231;
}
else
{
if ( v13 != -4 )
return 231;
*(_DWORD *)(address + 24) = 255;
*(_DWORD *)(address + 28) = 1;
}
v6 = *(_DWORD *)(address + 28);
v7 = *(_DWORD *)(address + 24);
v8 = v13 == -100;
v9 = (const char *)System::AnsiString::c_str(address + 4);
v19 = sub_46C370(v9, v8, v7, v6);
if ( (_BYTE)v19 == v14 )
{
if ( BYTE1(v19) == v15 )
{
if ( (unsigned __int8)(v19 >> 16) == v16 )
{
if ( BYTE3(v19) == v17 )
{
if ( v13 == -100 )
{
if ( a2 <= *(_DWORD *)(address + 24) )
result = 45; // ==45 0x2d时才正确
else
result = 78;
}
else
{
if ( v13 == -4 )
{
v18 = (v12 << 16) + (v11 << 8) + v10;
v18 = sub_46C5CD((v12 << 16) + (v11 << 8) + v10, v19);
if ( v18 )
{
*(_DWORD *)(address + 20) = v18;
result = 147;
}
else
{
result = 231;
}
}
else
{
result = 231;
}
}
}
else
{
result = 231;
}
}
else
{
result = 231;
}
}
else
{
result = 231;
}
}
else
{
result = 231;
}
return result;
}
可以看到在我注释的地方,就是返回45的情况。
正确的挑战指令在olldbg中如下:
引用:
0046B447 > \B8 2D000000 MOV EAX,2D
0046B44C . 8B55 D8 MOV EDX,DWORD PTR SS:[EBP-28]
0046B44F . 64:8915 00000>MOV DWORD PTR FS:[0],EDX
0046B456 . EB 73 JMP SHORT 010Edito.0046B4CB
所以,爆破就很简单了。直接在验证用户名,密码存在并且密码位数正确的地方通过后的指令改为到此的跳转:
即一些这句
引用:
0046B2B6 /0F85 83000000 JNZ 010Edito.0046B33F
,改成:
引用:
0046B2B6 . /E9 8C010000 JMP 010Edito.0046B447
0046B2BB |90 NOP
将修改写到可执行文件,就大功告成了!
事实上,我花了更多的时间希望研究出他的验证函数,IDA还原如下:
代码:
int __cdecl sub_46C370(const char *s, int a2, char a3, char a4)
{
int v4; // eax@2
size_t v6; // [sp+4h] [bp-10h]@1
signed int i; // [sp+8h] [bp-Ch]@1
int v8; // [sp+10h] [bp-4h]@1
v8 = 0;
v6 = strlen(s);
for ( i = 0; i < (signed int)v6; ++i )
{
v4 = toupper(s[i]);
if ( a2 )
v8 = word_5C91A8[(unsigned __int8)(15 * a4 + 13 * i)]
+ word_5C91A8[(unsigned __int8)(17 * a3 + 9 * i)]
+ word_5C91A8[(unsigned __int8)(19 * i)]
+ word_5C91A8[(unsigned __int8)(v4 + 47)] * (word_5C91A8[(unsigned __int8)(v4 + 13)] ^ (v8 + word_5C91A8[v4]));
else
v8 = word_5C91A8[(unsigned __int8)(15 * a4 + 13 * i)]
+ word_5C91A8[(unsigned __int8)(17 * a3 + 9 * i)]
+ word_5C91A8[(unsigned __int8)(7 * i)]
+ word_5C91A8[(unsigned __int8)(v4 + 23)] * (word_5C91A8[(unsigned __int8)(v4 + 63)] ^ (v8 + word_5C91A8[v4]));
}
return v8;
}
然后返回值需要满足上面sub_46B200中的一系列条件,不过很遗憾,分析还感觉有些困难,等以后功底更深了再分析算法吧~
下一篇: 通过反射将变量值转为变量名本身