前几天看了 zenghw 兄的 “SMC与算法结合的快速运用 VC++” 后,也有个类似的想法,于是这两天折腾了一个SMC的CM。
其中验证和弹出注册提示部分就是用SMC加密过的,只有输入正确的序列号才能还原成可执行的代码
主要是想验证下这样的SMC加密是否有效,所以欢迎爆破。加密方式比较随意,不过配合SMC,想来比较猥琐
// 验证输入 [COLOR="red"]00469088
procedure TForm1.KenGen(Name, Serial: String);
label
Err_Input;
var
S1, S2: Integer;
begin
if (Length(Name) < 4) then goto Err_Input; // 所以说用户名是摆设
if Length(Serial) <> 16 then goto Err_Input;
// 序列号16个字节,只能输入0-9,A-F (16进制数值),分成前后两部分存入S1, S2
// 如果 TryStrToInt() 转换失败(说明输入的序列号不是以上字符),跳向错误提示
if Not TryStrToInt('$'+Copy(Serial, 1, 8), S1) then goto Err_Input;
if Not TryStrToInt('$'+Copy(Serial, 9, 8), S2) then goto Err_Input;
{ 初始化 }
// 这里倒是我最头疼的事情,到底怎么给参数赋初值,混沌公式
// P(n+1) = A*P(n) * sin( 1-u*P(n) )
// 不用50轮,A*P(n) 就溢出了(中间存储用的double型),所以这里要缩小 P0 和 A 的值
Chaos_P0 := S1 shr 8;
// 反正是测试,限制A在0-255之间算了
// 注意:这样会使函数振幅过小,想稳妥点就要看大数计算方面的东西了
Chaos_A := ((S1 shl 3) or (S2 and $07)) and $FF;
Chaos_u := S2*0.23;
try
// 加密开始位置
{$I ChaosEncryptBegin.pas}
// 这个文件在编译时只是一个桩,处理后替换成调用解密函数的15字节
004691E7 . 68 FA000000 PUSH 0FA ; /dwLength = 000000FA (待解密块长度)
004691EC . 68 F6914600 PUSH SMC_Crac.004691F6 ; |dwStart = 004691F6 (起始地址)
004691F1 . E8 0AFEFFFF CALL SMC_Crac.00469000 ; \call SMC_DecryptBlock() (解密函数,这个后面再说)
// [COLOR="Blue"]这里没有用循环效验,而是利用了SEH的特性
// 解密的结果只有两种 可以执行 or 发生异常
// 这样就避免了用循环效验,没有效验值能够反推
// 再次验证一下P0或者别的值,看看是否正确解出
// ChainDataWithData(Chaos_P0, Addr) = $3EC12C17
if (Chaos_P0 = $3EC12C17) then
begin
// OK,注册提示
Timer_Tips.Enabled := False;
Label_Info.Caption := Format('[已注册] Name: %s, Serial:%s', ['TEST', Serial]);
asm
pushad
// Delphi的常量字符串处理起来比较麻烦,提示信息硬编码了
db $EB, 70
// -----------------
{+ 7} db '恭喜你',0 // 07
{+18} db '注册成功!',0 // 11
{+70} db '如果是爆破的,请务必将爆破方法告诉我。',13,10,'木桩(2009)',0,$90 // 52
call @@eip_caller
@@eip_caller:
pop ebx
push 0
sub ebx, 5+70 // Title
push ebx
add ebx, 7 // Text
push ebx
push 0
call MessageBoxA
push 0
push ebx
add ebx, 11
push ebx
push 0
call MessageBoxA
popad
end;
Panel1.Visible := False;
Form1.Height := Form1.Height - Panel1.Height;
end;
// 加密段结束标记
{$I ChaosEncryptEnd.pas}
except
// [COLOR="blue"]如果里面代码执行不正常,说明解密不正确
// [COLOR="blue"]会因为SEH跳到这里,给出个消息提示下
MessageBox(Handle, 'STOP!注册码错误。', '注册失败', MB_OK);
end;
// 插桩:还原之前解密的内容,不然注册码输错一次,就没法还原了
[COLOR="Purple"]// 附带的测试程序没有Patch掉这个桩,可以试试看
{$I ChaosEncryptEnd.pas}
Exit;
Err_Input:
MessageBox(Handle, '用户名或注册码错误!', '注册失败 :(', MB_OK);
end;
var
Chaos_P0: DWORD; // [全局变量]存放P0
Chaos_A, Chaos_u: Double; // [全局变量]混沌公式的参数A, u
// 将数据A与B相关
procedure ChainDataWithData(var A: DWORD; B: DWORD; cMode: Integer = 1); stdcall;
var
tmpD: DWORD;
begin
case cMode of
0: // 逆向还原
begin
// 循环右移3
asm
push eax
mov eax, B
ror eax, 11
mov tmpD, eax
pop eax
end;
A := A - tmpD;
end;
1: // 正向相关
begin
asm
push eax
mov eax, B
ror eax, 11
mov tmpD, eax
pop eax
end;
A := A + tmpD;
end;
end;
end;
// 由 P(n) 计算 P(n+1)
function ChaoStreamGener(Pn: DWORD): DWORD;
var
tmpExt: Double;
begin
// 最简单的混沌公式:P(n) = A*f(P(n-1))
// [COLOR="Red"]P(n+1) = A*P(n) * sin( 1-u*P(n) )
tmpExt := Chaos_A*Pn * Sin( 1-Chaos_u*Pn );
// 没办法,如果用double且不限制范围的话,不要几轮就溢出了
// 取整
Result := Abs(Round(tmpExt)) mod $FFFFFFFF;
end;
// Self Modifying Code Decrypt
procedure SMC_DecryptBlock(dwStart, dwLength: DWORD); stdcall;
label
Magic_End;
var
dwTLen: DWORD;
dbRnd: BYTE;
dwOldProtect: DWORD;
begin
if (Chaos_P0 = Chaos_Magic) then goto Magic_End;
dwTLen := dwLength div 4; // 对4取整,计算异或次数
// 计算地址与Key的相关数,放入Chaos_P0
ChainDataWithData(Chaos_P0, dwStart, 1);
// 开写保护 // PAGE_EXECUTE_READWRITE
asm
pushad
dw $310F // rdtsc - EAX=LO, EDX=HI
shr eax, 28
mov dbRnd, al // dbRnd = (LO shr 28)
popad
end;
// 加个随机数 dbRnd,让IDA看不到地址信息
VirtualProtect(Pointer(dwStart+dbRnd), 1, PAGE_READWRITE, @dwOldProtect);
asm
pushad
mov edx, dwStart
mov ecx, dwTLen
mov eax, Chaos_P0 // eax = 初值
@@Encrypt_Loop:
push edx
push ecx
push ebx
// 生成P(n)
call ChaoStreamGener // fastcall - IN eax, OUT eax
pop ebx
pop ecx
pop edx
xor dword ptr [edx], eax
add edx, 4 // 指向下一个DWORD
loop @@Encrypt_Loop // 继续
popad
end;
// 如果不是4的整数倍
// if (dwTLen*4 <> dwLength) then
// 不管了 :P
// 反正就1-3字节
// 关写保护
VirtualProtect(Pointer(dwStart+dbRnd), 1, dwOldProtect, @dwOldProtect);
Magic_End:
end;
[培训]科锐逆向工程师培训第53期2025年7月8日开班!