前言
各位大佬早上好,我是你们好朋友旺仔,今天我来分享一下,过反调试的方法以及使用IDA还原代码 + 写注册机的过程
由于内容太多,我准备分为两个帖子写,这个帖子主要是写IDA还原代码,下一个帖子是写反调试的分析以及过反调试和异常
这个CrackMe Level3是一个朋友发我的,我也不知道他在哪里弄的,我感觉挺好玩的,对反调试 异常 以及代码还原的学习有一些帮助
准备环节
反调试原文链接:https://blog.csdn.net/liuhaidon1992/article/details/103888101
C++异常处理的学习笔记:https://bbs.kanxue.com/thread-273332.htm
IDA如何使用:https://www.jianshu.com/p/ee0fcf93c8e7
调试器:X64和OD
反编译工具:IDA
PE工具:Detect It Easy
反调试插件:OD的StrongOD和虫子的ScyllaHide
ARK工具:Pchunter
MFC工具:MfcSpy
初步分析
第一步先看看PE文件 工具说:软件是使用2008的MFC写的

上面得知是MFC写的软件,我们可以利用MFC的RTTI(动态类型识别)获取信息,如果不是MFC写的,我们可以运行一下软件看看,有没有报错信息
当然这个软件,输入注册码点击注册没有任何提示

打开调试器附加看看字符串,结果软件会闪退,无法附加,估计是有反调试,遇到这种情况我有三种方式可以解决
第一种:静态分析解决反调试
第二种:使用ARK工具把软件暂停,然后在用调试器附加
第三种:直接使用调试器打开程序,让软件断到入口或者系统断点
我们先使用第二种吧,因为X32调试器对中文字符串的支持不是很好,我没有装插件,所以先用OD看看字符串吧

验证函数中有一个异常 OD的反汇编已经出现了BUG,可以使用删除分析,我这里就不删除了 直接打开IDA F5吧


可以看到IDA的F5也被废掉了的

虽然不能F5 但是我们可以很清晰的看到,两个GetWindowText 估计一个是获取用户名 一个是获取密码

至于为什么不能F5 是因为有一个jmp [ebx + 0xXXX] 这样的代码 IDA在解析代码的时候 它不知道跳到那里去 导致无法解析

我们直接把这个Jmp [ebp + var_8A8] 给nop掉就好了 然后删除这个函数的分析 接着在把这个函数分析为代码即可
我们可以向上看,在OD反汇编的时候也是到这个位置 反汇编失败了

下面是IDA F5后的代码 很明显 这IDA还原出来的根本不能用,这是需要我们进行修理变量的,另外我们给它重新命名一下
int __thiscall sub_401810(CWnd *this)
{
CWnd *DlgItem; // eax
CWnd *v2; // eax
int result; // eax
char *v4; // [esp+10h] [ebp-8C8h]
CHAR *v5; // [esp+14h] [ebp-8C4h]
unsigned int v6; // [esp+18h] [ebp-8C0h]
unsigned __int8 v8; // [esp+21h] [ebp-8B7h]
unsigned __int8 v9; // [esp+22h] [ebp-8B6h]
unsigned __int8 v10; // [esp+23h] [ebp-8B5h]
int k; // [esp+24h] [ebp-8B4h]
int j; // [esp+28h] [ebp-8B0h]
int i; // [esp+2Ch] [ebp-8ACh]
int v14; // [esp+34h] [ebp-8A4h]
int v15; // [esp+34h] [ebp-8A4h]
char v16; // [esp+38h] [ebp-8A0h] BYREF
int v17[6]; // [esp+39h] [ebp-89Fh] BYREF
char v18; // [esp+51h] [ebp-887h]
char v19; // [esp+53h] [ebp-885h]
char v20; // [esp+54h] [ebp-884h]
int v21; // [esp+55h] [ebp-883h]
int v22[5]; // [esp+59h] [ebp-87Fh] BYREF
char v23; // [esp+6Dh] [ebp-86Bh]
int v24; // [esp+70h] [ebp-868h]
char v25; // [esp+74h] [ebp-864h]
int v26; // [esp+75h] [ebp-863h]
int v27; // [esp+79h] [ebp-85Fh]
int v28; // [esp+7Dh] [ebp-85Bh]
int v29; // [esp+81h] [ebp-857h]
int v30; // [esp+85h] [ebp-853h]
int v31; // [esp+89h] [ebp-84Fh]
char v32; // [esp+8Dh] [ebp-84Bh]
char v33; // [esp+93h] [ebp-845h]
char v34; // [esp+94h] [ebp-844h]
int v35; // [esp+95h] [ebp-843h]
int v36[5]; // [esp+99h] [ebp-83Fh] BYREF
char v37; // [esp+ADh] [ebp-82Bh]
CHAR String[1028]; // [esp+B0h] [ebp-828h] BYREF
int v39; // [esp+4B4h] [ebp-424h]
CHAR v40[1028]; // [esp+4B8h] [ebp-420h] BYREF
CPPEH_RECORD ms_exc; // [esp+8C0h] [ebp-18h]
MEMORY[0] = 6530;
ms_exc.registration.TryLevel = -2;
v33 = sub_402D60();
v14 = sub_402C60(v33);
sub_402750(v14);
v26 = 0;
v27 = 0;
v28 = 0;
v29 = 0;
v30 = 0;
v31 = 0;
v32 = 0;
v35 = 0;
memset(v36, 0, sizeof(v36));
v37 = 0;
v21 = 0;
memset(v22, 0, sizeof(v22));
v23 = 0;
v25 = 48;
v34 = 66;
v20 = 98;
for ( i = 1; i < 26; ++i )
{
*(&v25 + i) = *((_BYTE *)&v24 + i + 3) + 1;
*(&v34 + i) = *(&v33 + i) + 1;
*(&v20 + i) = *(&v19 + i) + 1;
}
CWnd::UpdateData(this, 1);
memset(String, 0, 1024);
memset(v40, 0, 1024);
ms_exc.registration.Next = (struct _EH3_EXCEPTION_REGISTRATION *)1024;
ms_exc.exc_ptr = (EXCEPTION_POINTERS *)String;
DlgItem = CWnd::GetDlgItem(this, 1000);
CWnd::GetWindowTextA(DlgItem, (LPSTR)ms_exc.exc_ptr, (int)ms_exc.registration.Next);
ms_exc.registration.Next = (struct _EH3_EXCEPTION_REGISTRATION *)1024;
ms_exc.exc_ptr = (EXCEPTION_POINTERS *)v40;
v2 = CWnd::GetDlgItem(this, 1001);
result = CWnd::GetWindowTextA(v2, (LPSTR)ms_exc.exc_ptr, (int)ms_exc.registration.Next);
if ( String[7] )
{
result = String[8];
if ( !String[8] && v40[23] && !v40[24] )
{
v39 = 16;
v24 = 32;
for ( j = 0; j < 8; ++j )
{
String[j] ^= j;
String[j] ^= *(_BYTE *)(j + v39);
String[j] ^= *(_BYTE *)(j + v24);
}
v16 = 0;
memset(v17, 0, sizeof(v17));
v18 = 0;
for ( k = 0; k < 8; ++k )
{
v10 = (unsigned __int8)(String[k] & 0xE0) / 32;
v8 = (String[k] & 0x1C) / 4;
v9 = String[k] & 3;
if ( k % 3 == 2 )
{
*(&v16 + 3 * k) = *(&v25 + v9);
*((_BYTE *)v17 + 3 * k) = *((_BYTE *)v36 + v10 + 3);
*((_BYTE *)v17 + 3 * k + 1) = *((_BYTE *)&v22[2] + v8 + 3);
}
if ( k % 3 == 1 )
{
*(&v16 + 3 * k) = *((_BYTE *)&v36[2] + v10 + 3);
*((_BYTE *)v17 + 3 * k) = *((_BYTE *)v22 + v8 + 3);
*((_BYTE *)v17 + 3 * k + 1) = *(&v25 + v9);
}
if ( !(k % 3) )
{
*(&v16 + 3 * k) = *((_BYTE *)&v36[2] + v8 + 3);
*((_BYTE *)v17 + 3 * k) = *((_BYTE *)v22 + v9 + 3);
*((_BYTE *)v17 + 3 * k + 1) = *(&v25 + v10);
}
}
sub_401790();
v33 = sub_402D90();
v15 = sub_402C60(v33);
sub_4028A0(v15);
v6 = 24;
v5 = v40;
v4 = &v16;
while ( v6 >= 4 )
{
result = (int)v5;
if ( *(_DWORD *)v4 != *(_DWORD *)v5 )
return result;
v6 -= 4;
v5 += 4;
v4 += 4;
}
return CWnd::MessageBoxA(this, &Text, 0, 0);
}
}
return result;
}
1.根据循环可以知道对应数组大小和数组首地址
2.根据MemSet也可以知道对应数组的大小和数组首地址
3.根据GetWindowsTest也可以知道数组的大小
修正变量要结合代码怎么去操作这个变量的 根据它的逻辑来猜测大小


这是变量重命名后的汇编代码

经过修理后的IDA F5代码 可以看到基本上很清晰了 效果跟源代码应该很接近
int __thiscall sub_401810(CWnd *this)
{
CWnd *DlgItem; // eax
CWnd *v2; // eax
int result; // eax
char *pFinalKey_1; // [esp+10h] [ebp-8C8h]
char *pGetWindowTextString1_1; // [esp+14h] [ebp-8C4h]
unsigned int Var24_1; // [esp+18h] [ebp-8C0h]
unsigned __int8 Var8_1; // [esp+21h] [ebp-8B7h]
unsigned __int8 Var9_1; // [esp+22h] [ebp-8B6h]
unsigned __int8 Var10_1; // [esp+23h] [ebp-8B5h]
int K; // [esp+24h] [ebp-8B4h]
int J; // [esp+28h] [ebp-8B0h]
int I; // [esp+2Ch] [ebp-8ACh]
int Unknown2_1; // [esp+34h] [ebp-8A4h]
int unknown1; // [esp+34h] [ebp-8A4h]
char FinalKey_1[26]; // [esp+38h] [ebp-8A0h] BYREF
char SecretKey3[28]; // [esp+54h] [ebp-884h] BYREF
int v19; // [esp+70h] [ebp-868h]
char SecretKey1[31]; // [esp+74h] [ebp-864h] BYREF
char Unknown3_1; // [esp+93h] [ebp-845h]
char SecretKey2[28]; // [esp+94h] [ebp-844h] BYREF
char GetWindowTextString2_1[1028]; // [esp+B0h] [ebp-828h] BYREF
int v24; // [esp+4B4h] [ebp-424h]
char GetWindowTextString1[1028]; // [esp+4B8h] [ebp-420h] BYREF
CPPEH_RECORD ms_exc; // [esp+8C0h] [ebp-18h]
MEMORY[0] = 6530;
ms_exc.registration.TryLevel = -2;
Unknown3_1 = sub_402D60();
Unknown2_1 = sub_402C60(&dword_42FA04, Unknown3_1);
sub_402750(Unknown2_1);
memset(&SecretKey1[1], 0, 25);
memset(&SecretKey2[1], 0, 25);
memset(&SecretKey3[1], 0, 25);
SecretKey1[0] = 48;
SecretKey2[0] = 66;
SecretKey3[0] = 98;
for ( I = 1; I < 26; ++I )
{
SecretKey1[I] = SecretKey1[I - 1] + 1;
SecretKey2[I] = SecretKey2[I - 1] + 1;
SecretKey3[I] = SecretKey3[I - 1] + 1;
}
CWnd::UpdateData(this, 1);
memset(GetWindowTextString2_1, 0, 1024);
memset(GetWindowTextString1, 0, 1024);
ms_exc.registration.Next = (struct _EH3_EXCEPTION_REGISTRATION *)1024;
ms_exc.exc_ptr = (EXCEPTION_POINTERS *)GetWindowTextString2_1;
DlgItem = CWnd::GetDlgItem(this, 1000);
CWnd::GetWindowTextA(DlgItem, (LPSTR)ms_exc.exc_ptr, (int)ms_exc.registration.Next);
ms_exc.registration.Next = (struct _EH3_EXCEPTION_REGISTRATION *)1024;
ms_exc.exc_ptr = (EXCEPTION_POINTERS *)GetWindowTextString1;
v2 = CWnd::GetDlgItem(this, 1001);
result = CWnd::GetWindowTextA(v2, (LPSTR)ms_exc.exc_ptr, (int)ms_exc.registration.Next);
if ( GetWindowTextString2_1[7] )
{
result = GetWindowTextString2_1[8];
if ( !GetWindowTextString2_1[8] && GetWindowTextString1[23] && !GetWindowTextString1[24] )
{
v24 = 16;
v19 = 32;
for ( J = 0; J < 8; ++J )
{
GetWindowTextString2_1[J] ^= J;
GetWindowTextString2_1[J] ^= *(_BYTE *)(J + v24);
GetWindowTextString2_1[J] ^= *(_BYTE *)(J + v19);
}
memset(FinalKey_1, 0, sizeof(FinalKey_1));
for ( K = 0; K < 8; ++K )
{
Var10_1 = (unsigned __int8)(GetWindowTextString2_1[K] & 0xE0) / 32;
Var8_1 = (GetWindowTextString2_1[K] & 0x1C) / 4;
Var9_1 = GetWindowTextString2_1[K] & 3;
if ( K % 3 == 2 )
{
FinalKey_1[3 * K] = SecretKey1[Var9_1];
FinalKey_1[3 * K + 1] = SecretKey2[Var10_1 + 8];
FinalKey_1[3 * K + 2] = SecretKey3[Var8_1 + 16];
}
if ( K % 3 == 1 )
{
FinalKey_1[3 * K] = SecretKey2[Var10_1 + 16];
FinalKey_1[3 * K + 1] = SecretKey3[Var8_1 + 8];
FinalKey_1[3 * K + 2] = SecretKey1[Var9_1];
}
if ( !(K % 3) )
{
FinalKey_1[3 * K] = SecretKey2[Var8_1 + 16];
FinalKey_1[3 * K + 1] = SecretKey3[Var9_1 + 8];
FinalKey_1[3 * K + 2] = SecretKey1[Var10_1];
}
}
sub_401790();
Unknown3_1 = sub_402D90();
unknown1 = sub_402C60(&dword_42FA04, Unknown3_1);
sub_4028A0(unknown1);
Var24_1 = 24;
pGetWindowTextString1_1 = GetWindowTextString1;
pFinalKey_1 = FinalKey_1;
while ( Var24_1 >= 4 )
{
result = (int)pGetWindowTextString1_1;
if ( *(_DWORD *)pFinalKey_1 != *(_DWORD *)pGetWindowTextString1_1 )
return result;
Var24_1 -= 4;
pGetWindowTextString1_1 += 4;
pFinalKey_1 += 4;
}
return CWnd::MessageBoxA(this, &Text, 0, 0);
}
}
return result;
}
我们可以看到上面的代码中还是有两个变量的值是错误的 一个是V24 一个是V19 为什么是错误的呢?
我们可以结合下面的循环代码,下面代码中用到V24 是*(BYTE*)(J+V24); 用到V19 是*(BYTE*)(J+V19);
这很明显是一个数组 大小是8个字节
看下面代码中 V19和V24是来源于AbnormalVariable这个变量的值 + 16和+32的位置的

这也就是下面代码中V19和V24数组的值的来源

最终的C代码 算法还原完成
int main()
{
char SecretKey1[28];
char SecretKey2[28];
char SecretKey3[28];
memset(SecretKey1,0,sizeof(SecretKey1));
memset(SecretKey2,0,sizeof(SecretKey2));
memset(SecretKey3,0,sizeof(SecretKey3));
SecretKey1[0] = 48;
SecretKey2[0] = 66;
SecretKey3[0] = 98;
for (int I = 1; I < 26; ++I)
{
SecretKey1[I] = SecretKey1[I - 1] + 1;
SecretKey2[I] = SecretKey2[I - 1] + 1;
SecretKey3[I] = SecretKey3[I - 1] + 1;
}
char GetWindowTextString1[1024];
char GetWindowTextString2_1[1024];
memset(GetWindowTextString1,0,1024);
memset(GetWindowTextString2_1, 0, 1024);
strcpy(GetWindowTextString2_1,"12345678");
char v24[24] = { 0x00,0x00,0x88,0x85,0xBB,0xF7,0xFF,0xFF,0x0F,0xB6,0x8D,0xBB,0xF7,0xFF,0xFF,0xB8,0x04,0xFA,0x42,0x00,0xE8,0xAD };
char v19[24] = { 0xB8,0x04,0xFA,0x42,0x00,0xE8,0xAD,0x13,0x00,0x00,0x89,0x85,0x5C,0xF7,0xFF,0xFF,0x8B,0x95,0x5C,0xF7,0xFF,0xFF };
for (int J = 0; J < 8; ++J)
{
GetWindowTextString2_1[J] ^= J;
GetWindowTextString2_1[J] ^= v24[J];
GetWindowTextString2_1[J] ^= v19[J];
}
char FinalKey_1[26];
memset(FinalKey_1,0,sizeof(FinalKey_1));
for (int K = 0; K < 8; ++K)
{
char Var10_1 = (unsigned __int8)(GetWindowTextString2_1[K] & 0xE0) / 32;
char Var8_1 = (GetWindowTextString2_1[K] & 0x1C) / 4;
char Var9_1 = GetWindowTextString2_1[K] & 3;
if (K % 3 == 2)
{
FinalKey_1[3 * K] = SecretKey1[Var9_1];
FinalKey_1[3 * K + 1] = SecretKey2[Var10_1 + 8];
FinalKey_1[3 * K + 2] = SecretKey3[Var8_1 + 16];
}
if (K % 3 == 1)
{
FinalKey_1[3 * K] = SecretKey2[Var10_1 + 16];
FinalKey_1[3 * K + 1] = SecretKey3[Var8_1 + 8];
FinalKey_1[3 * K + 2] = SecretKey1[Var9_1];
}
if (!(K % 3))
{
FinalKey_1[3 * K] = SecretKey2[Var8_1 + 16];
FinalKey_1[3 * K + 1] = SecretKey3[Var9_1 + 8];
FinalKey_1[3 * K + 2] = SecretKey1[Var10_1];
}
}
//FinalKey_1 = 0x00aff3f8 "Tk4So33LrVj7Vl20KuRm3Xn3"
system("pause");
return 0;
}
用户名:12345678
注册码:Tk4So33LrVj7Vl20KuRm3Xn3

二进制系列之Pwn篇
最后于 4天前
被旺仔_小可爱编辑
,原因: