CM160
CM160#
尝试解决吾爱的160个CrackMe,陆续更新
Acid burn#
单Serial功能,直接做字符串比较
Name&Serial功能
注册机
package main
import (
"fmt"
)
func main() {
input := "Dawn"
if len(input) > 0 {
firstLetter := input[0] // 获取第一个字母
pass := int(firstLetter)*41*2
hexValue := fmt.Sprintf("CW-%d-CRACKED", pass) // 将字母转换为十六进制字符串
fmt.Println("密码:", hexValue)
}
}
AfKayAs CrackMe #1#
判断部分,包含拼接激活码
00402510 | mov eax,dword ptr ss:[ebp-18] | [ebp-18]:L"Type In Your Serial"
00402513 | mov ecx,dword ptr ss:[ebp-1C] | [ebp-1C]:L"1658111"
00402516 | mov edi,dword ptr ds:[<&__vbaStrCat>] | 做结果拼接
0040251C | push eax | eax:L"AKA-1658111"
0040251D | push afkayas.1.401B70 | 401B70:L"AKA-"
00402522 | push ecx |
00402523 | call edi |
00402525 | mov ebx,dword ptr ds:[<&__vbaStrMove>] |
0040252B | mov edx,eax |
0040252D | lea ecx,dword ptr ss:[ebp-20] |
00402530 | call ebx |
00402532 | push eax |
00402533 | call dword ptr ds:[<&__vbaStrCmp>] | 字符串比较
00402539 | mov esi,eax |
从函数入口[00402310]开始,步过,[004024F] 第一次看到生成码
00402412 | push eax | eax:L"Type In Your Name"
00402413 | mov ebx,dword ptr ds:[edx] |
00402415 | call dword ptr ds:[<&__vbaLenBstr>] | 长度
0040241B | mov edi,eax |
0040241D | mov ecx,dword ptr ss:[ebp-18] |
00402420 | imul edi,edi,17CFB | 长度*17CFB
00402426 | push ecx |
00402427 | jo afkayas.1.4026BE |
0040242D | call dword ptr ds:[<&rtcAnsiValueBstr>] | 名字首字母
00402433 | movsx edx,ax |
00402436 | add edi,edx | 首字母+长度*17CFB
00402438 | jo afkayas.1.4026BE |
0040243E | push edi |
0040243F | call dword ptr ds:[<&__vbaStrI4>] | 转换为Dec字符串
00402445 | mov edx,eax |
流程完成
package main
import (
"fmt"
)
func main() {
input := "Dawn"
if len(input) > 0 {
le := len(input)
firstLetter := input[0]
pass := int(firstLetter) + le*97531
hexValue := fmt.Sprintf("AKA-%d", pass)
fmt.Println("密码:", hexValue)
}
}
AfKayAs CrackMe #2#
目标:禁用延迟弹窗并注册
能通过VBDec看到相关信息,弹窗取消,一个是更改GUI启动顺序,将弹窗放置注册程序后,另一种是更改Timer时间.
更改弹窗顺序,要通过修改VBGUITable完成,需要从VBHeader找到GUITable的指针,在VBHeader结构中偏移为4C,VBHeader地址来源为程序入口调用ThunRTMain之前的push,相关知识参考链接
Visual Basic程序的逆向分析 - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
typedef struct{
char Signature[4]; //00H 四个字节的签名符号,和PEHEADER里的那个signature是类似性质的东西,VB文件都是"VB5!"
WORD RtBuild; //04H 运行时创立的变量(类似编译的时间)
BYTE LangDLL[14]; //06H 语言DLL文件的名字(如果是0x2A的话就代表是空或者是默认的)
BYTE BakLangDLL[14]; //14H 备份DLL语言文件的名字(如果是0x7F的话就代表是空或者是默认的,改变这个值堆EXE文件的运行没有作用)
WORD RtDLLVer; //22H 运行时DLL文件的版本
DWORD LangID; //24H 语言的ID
DWORD BakLangID; //28H 备份语言的ID(只有当语言ID存在时它才存在)
DWORD pSubMain; //2CH RVA(实际研究下来是VA) sub main过程的地址指针(3.)(如果时00000000则代表这个EXE时从FORM窗体文件开始运行的)
DWORD pProjInfo; //30H VA 工程信息的地址指针,指向一个ProjectInfo_t结构(2.)
DWORD fMDLIntObjs; //34H ?详细见"MDL 内部组建的标志表"
DWORD fMDLIntObjs2; //36H ?详细见"MDL 内部组建的标志表"
DWORD ThreadFlags; //38H 线程的标志
//* 标记的定义(ThreadFlags数值的含义)
//+-------+----------------+--------------------------------------------------------+
//| 值 | 名字 | 描述
//+-------+----------------+--------------------------------------------------------+
//| 0x01 | ApartmentModel | 特别化的多线程使用一个分开的模型
//| 0x02 | RequireLicense | 特别化需要进行认证(只对OCX)
//| 0x04 | Unattended | 特别化的没有GUI图形界面的元素需要初始化
//| 0x08 | SingleThreaded | 特别化的静态区时单线程的
//| 0x10 | Retained | 特别化的将文件保存在内存中(只对Unattended)
//+-------+----------------+--------------------------------------------------------+
//ex: 如果是0x15就表示是一个既有多线程,内存常驻,并且没有GUI元素要初始化
DWORD ThreadCount; //3CH 线程个数
WORD FrmCount; //41H 窗体个数
WORD pExternalComponentCount; //44H VA 外部引用个数例如WINSOCK组件的引用
DWORD ThunkCount; //48H ?大概是内存对齐相关的东西
DWORD GUITable; //4CH VA GUI元素表的地址指针(指向一个GUITable_t结构)
DWORD pExternalComponentTable; //50H VA 外部引用表的地址指针
// DWORD pProjDep; // VA 工程的描述的地址指针(这个其实没有)
DWORD pComRegData; //54H VA COM注册数据的地址指针
DWORD oProjExename; //58H Offset 指向工程EXE名字的字符串
DWORD oProjTitle; //5CH Offset 指向工程标题的字符串
DWORD oHelpFile; //60H Offset 指向帮助文件的字符串
DWORD oProjName; //64H Offset 指向工程名的字符串
}VBHeader_t;
GUITable部分指向的结构含义如下
Signature DWORD //00H.必须是50000000
FomID TGUID //04,可能是以GUID方式命名的formID
Index BYTE //24H 窗体的序号
Flag1 BYTE //28H 第一个窗体的启动标志,可能是90 也可能是10
AGUIDescriptionTable DWORD //48H指针指向以“FFCC…“开始的FormGUI表
Flag3 Dword //4CH.意义不明
位置可以自行验证
之后是破解注册,后续调试输入为"Type In Your Name",密码有数据输入要求,必须为数字,为"123456",逻辑和上一次有些像,先定位这里
往下看 有别的生成和判断结构
VBDecompliner 的反编译结果和IDA对比分析,#1的计算的部分不再重复
$$ str[0] +len(str)*8888 $$
从+(10/5)的部分开始,其中 10/5是通过vbaR8Str函数将之前的运算存入st ,通过fdiv和faddp完成
$$ str[0] +len(str)*8888+2 $$
004082E3 | mov edx,dword ptr ss:[ebp-18] | [ebp-18]:L"1511180"
004082E6 | push edx |
004082E7 | mov ebx,dword ptr ds:[ecx] |
004082E9 | call dword ptr ds:[<&__vbaR8Str>] | 1511180压入st
004082EF | fld st(0),dword ptr ds:[401008] | 10 压入st
004082F5 | cmp dword ptr ds:[409000],0 |
004082FC | jne afkayas.2.408306 |
004082FE | fdiv st(0),dword ptr ds:[40100C] | 用10/5 存入st0
00408304 | jmp afkayas.2.408311 |
00408306 | push dword ptr ds:[40100C] |
0040830C | call <JMP.&_adj_fdiv_m32> |
00408311 | sub esp,8 |
00408314 | fnstsw ax |
00408316 | test al,D |
00408318 | jne afkayas.2.4087BF |
0040831E | faddp st(1),st(0) | st0=st1+st0
00408320 | fnstsw ax |
00408322 | test al,D |
00408324 | jne afkayas.2.4087BF |
0040832A | fstp qword ptr ss:[esp],st(0) | 保存 "1511182"
0040832D | call dword ptr ds:[<&__vbaStrR8>] |
相同的,在[004083F8]进行了下一次计算,当前逻辑为
$$ (str[0] +len(str)*8888+2)*3-2 $$
004083EF | mov edx,dword ptr ss:[ebp-18] | [ebp-18]:L"1511182"
004083F2 | push edx |
004083F3 | mov ebx,dword ptr ds:[ecx] |
004083F5 | call dword ptr ds:[<&__vbaR8Str>] |
004083FB | fmul st(0),qword ptr ds:[401010] | 1511182*3
00408401 | sub esp,8 |
00408404 | fsub st(0),qword ptr ds:[401018] | 1511182*3-2
0040840A | fnstsw ax |
0040840C | test al,D |
0040840E | jne afkayas.2.4087BF |
00408414 | fstp qword ptr ss:[esp],st(0) | 保存
00408417 | call dword ptr ds:[<&__vbaStrR8>] |
之后又加了一次15
$$ (str[0] +len(str)*8888+2)*3-2- (-15) $$
004084E5 | fsub st(0),qword ptr ds:[401020] | 4533544-(-15)
004084EB | sub esp,8 |
004084EE | fnstsw ax |
004084F0 | test al,D |
004084F2 | jne afkayas.2.4087BF |
004084F8 | fstp qword ptr ss:[esp],st(0) |
最后将结果和用户输入Key相除比较商,通过fdiv,fcomp实现,相关汇编指令知识
004085CE | mov eax,dword ptr ss:[ebp-18] | [ebp-18]:L"123456"
004085D1 | push eax |
004085D2 | call dword ptr ds:[<&__vbaR8Str>] | 123456 压入st
004085D8 | mov ecx,dword ptr ss:[ebp-1C] | [ebp-1C]:L"4533559"
004085DB | fstp qword ptr ss:[ebp-E4],st(0) |
004085E1 | push ecx |
004085E2 | call dword ptr ds:[<&__vbaR8Str>] | 4533559 压入st
004085E8 | cmp dword ptr ds:[409000],0 |
004085EF | jne afkayas.2.4085F9 |
004085F1 | fdivr st(0),qword ptr ss:[ebp-E4] | 123456/4533559
004085F7 | jmp afkayas.2.40860A |
004085F9 | push dword ptr ss:[ebp-E0] |
004085FF | push dword ptr ss:[ebp-E4] |
00408605 | call <JMP.&_adj_fdivr_m64> |
0040860A | fnstsw ax |
0040860C | test al,D |
0040860E | jne afkayas.2.4087BF |
00408614 | call dword ptr ds:[<&__vbaFpR8>] |
0040861A | fcomp st(0),qword ptr ds:[401028] | 与1做比较
00408620 | fnstsw ax | 将结果标志位存至ax
00408622 | test ah,40 | 如果不相等 esi = 0
00408625 | je afkayas.2.40862E |
00408627 | mov esi,1 | 如果相等 es = 1
0040862C | jmp afkayas.2.408630 |
0040862E | xor esi,esi | esi = 0
...........
00408653 | neg esi |
00408655 | add esp,C |
00408658 | mov ecx,80020004 |
0040865D | mov eax,A |
00408662 | mov dword ptr ss:[ebp-64],ecx |
00408665 | test si,si | 判断si
00408668 | mov dword ptr ss:[ebp-6C],eax |
0040866B | mov dword ptr ss:[ebp-54],ecx |
0040866E | mov dword ptr ss:[ebp-5C],eax |
00408671 | mov dword ptr ss:[ebp-44],ecx |
00408674 | mov dword ptr ss:[ebp-4C],eax |
00408677 | je afkayas.2.4086DB | 判断跳转部分
00408679 | mov esi,dword ptr ds:[<&__vbaStrCat>] |
0040867F | push afkayas.2.406FC0 | 406FC0:L"You Get It"
00408684 | push afkayas.2.406FDC |
...........
004086DB | mov esi,dword ptr ds:[<&__vbaStrCat>] |
004086E1 | push afkayas.2.407008 | 407008:L"You Get Wrong"
004086E6 | push afkayas.2.406FDC |
004086EB | call esi |
整体流程结束
package main
import (
"fmt"
)
func main() {
input := "Dawn"
if len(input) > 0 {
le := len(input)
firstLetter := input[0]
pass := (int(firstLetter) + le*88888+2)*3-2+15
hexValue := fmt.Sprintf("%d", pass)
fmt.Println("密码:", hexValue)
}
}
CKme#
String看到注册成功
转到代码,没有验证按钮,自循环结构可能是注册判断,跟入
<img title="" src=“imgs/2024-01-23-11-08-12-image.png)
调试看是一个附近代码没有操作过的内存,应该不是在点击验证时生成的注册码
使用工具,查看有相关页面结构,可以双击定位,在工具查看反汇编
2f8偏移存储用户名长度
chkcode[00457c40]为主要验证注册码部分
00457C40 | push ebp |
......
00457C66 | mov esi,dword ptr ds:[ebx+2F8] | 用户名长度
00457C6C | add esi,5 | 长度+5
00457C6F | push dword ptr ds:[ebx+310] | [ebx+310]:"黑头Sun Bird"
00457C75 | lea edx,dword ptr ss:[ebp-8] |
00457C78 | mov eax,esi |
00457C7A | call IntToStr |
00457C7F | push dword ptr ss:[ebp-8] |
00457C82 | push dword ptr ds:[ebx+314] | [ebx+314]:"dseloffc-012-OK"
00457C88 | lea edx,dword ptr ss:[ebp-C] |
00457C8B | mov eax,dword ptr ds:[ebx+2D4] | 输入框1
00457C91 | call TContorl.GetText |
00457C96 | push dword ptr ss:[ebp-C] |
00457C99 | lea eax,dword ptr ds:[ebx+318] |
00457C9F | mov edx,4 |
00457CA4 | call LSTRCATN |
00457CA9 | xor edx,edx |
00457CAB | mov eax,dword ptr ds:[ebx+2F4] | Form1.Label6:Tlable1
00457CB1 | call TControl.SetVisible | 设置图片不可见
00457CB6 | mov edx,dword ptr ds:[ebx+318] | [ebx+318]:"黑头Sun Bird10dseloffc-012-OKtest2"
00457CBC | mov eax,dword ptr ds:[ebx+2F4] |
00457CC2 | call ckme.423378 |
...... 中间的19次循环我认为是脏代码
00457D1E | lea edx,dword ptr ss:[ebp-20] |
00457D21 | mov eax,dword ptr ds:[ebx+2D8] | 输入框2的值
00457D27 | call TControl.GetText |
00457D2C | mov eax,dword ptr ss:[ebp-20] |
00457D2F | mov edx,dword ptr ds:[ebx+318] | [ebx+318]:"黑头Sun Bird10dseloffc-012-OKtest2"
00457D35 | call StrCmp | StrCmp(输入框,[ebx+318])
00457D3A | jne ckme.457D46 | 判断
00457D3C | mov dword ptr ds:[ebx+30C],3E | 相等[ebx+30C] = 3E 注册成功
00457D46 | mov eax,dword ptr ds:[ebx+30C] |
00457D4C | add eax,10 |
00457D4F | mov dword ptr ds:[ebx+2FC],eax | [ebx+2FC] = 4E
00457D55 | add eax,23 |
00457D58 | mov dword ptr ds:[ebx+300],eax | [ebx+300] = 71
...... 依旧是19次循环
00457DB5 | mov eax,dword ptr ds:[ebx+2FC] | 4E
00457DBB | add eax,dword ptr ds:[ebx+300] | 4E+71 = BF
00457DC1 | mov dword ptr ds:[ebx+304],eax | [ebx+304] = BF
00457DC7 | mov edx,dword ptr ds:[ebx+2FC] |
00457DCD | add edx,9 | 4E+9
00457DD0 | add edx,eax | 4E+9+BF = 116
00457DD2 | mov dword ptr ds:[ebx+308],edx | [ebx+308] = 116
$$ “黑头Sun Bird” + (len(str)+5)+“dseloffc-012-OK”+str $$
之后看验证,也解释了为什么需要点击多次才能正常弹框,双击触发初步验证,再单击触发弹出图片,严格逻辑是 先双击再单击
双击触发
...... 脏代码
00457EF5 | cmp dword ptr ds:[esi+30C],3E | 判断是否为3E
00457EFC | jne ckme.457F08 |
00457EFE | mov dword ptr ds:[esi+30C],85 | 是的话,赋值 85
...... 脏代码
单击触发
00458031 | cmp dword ptr ds:[esi+30C],85 | 判断85
0045803B | jne ckme.4580B3 | 不匹配 结束
...... 脏代码
00458096 | mov eax,dword ptr ds:[esi+2F0] | TForm1.Panel1:TPanel
0045809C | call Tcontrol.SetVisible | 设置可见
004580A1 | mov eax,dword ptr ds:[45B820] | TForm1
004580A6 | add eax,70 |
004580A9 | mov edx,ckme.458114 | 恭喜恭喜。注册成功
004580AE | call ckme.403950 |
注册机
package main
import (
"fmt"
)
func main() {
input := "Dawn"
if len(input) > 0 {
le := len(input)+5
Key := fmt.Sprintf("黑头Sun Bird%ddseloffc-012-OK%s",le,input)
fmt.Println("密码:", Key)
}
}
CKMe002#
UPX壳,X32Dbg使用Scylla脱壳后,放入IDR
找到注册成功处判断,查看对应的触发函数,是一个OnTimer事件,即定时触发,查看汇编代码,有5处判断逻辑,只有都通过,才能进入成功注册
004473E4 | push ebx |
004473E5 | mov ebx,eax |
004473E7 | cmp dword ptr ds:[ebx+304],C34 | [ebx+304] != C34
004473F1 | je ckme002_dump_scy.44747F |
004473F7 | cmp dword ptr ds:[ebx+308],230D | [ebx+308] != 230D
00447401 | je ckme002_dump_scy.44747F |
00447403 | cmp dword ptr ds:[ebx+310],F94 | [ebx+310] == F94
0044740D | jne ckme002_dump_scy.44747F |
0044740F | mov eax,dword ptr ds:[ebx+318] |
00447415 | cmp eax,dword ptr ds:[ebx+314] | [ebx+314] == [ebx+318]
0044741B | jne ckme002_dump_scy.44747F |
0044741D | cmp dword ptr ds:[ebx+31C],3E7 | [ebx+31C] == 3E7
00447427 | je ckme002_dump_scy.44747F |
00447429 | xor edx,edx |
0044742B | mov eax,dword ptr ds:[ebx+2D8] | TForm1.Iamge1
00447431 | mov ecx,dword ptr ds:[eax] |
00447433 | call TControl.SetEnabled | 取消图片的点击事件
...... 四个图片依次取消,代码相同
0044745D | mov eax,dword ptr ds:[4498A8] |
00447462 | add eax,70 | TForm1.Hint
00447465 | mov edx,ckme002_dump_scy.44748C | 厉害厉害真厉害
0044746A | call @LStrAsg |
0044746F | mov edx,ckme002_dump_scy.4474B8 | 注册了
00447474 | mov eax,dword ptr ds:[ebx+2EC] | TForm1.Button1
0044747A | call TControl.SetText |
转到FormCreate函数,可以看到一些对于提到位置的操作
...... 设置图片不可见blablabla
00446C89 | xor edx,edx |
00446C8B | mov eax,dword ptr ds:[ebx+2F0] | TForm1.Edit2
00446C91 | mov ecx,dword ptr ds:[eax] |
00446C93 | call dword ptr ds:[ecx+5C] | 禁用第二个输入框TControl.SetEnabled
...... 设置图片位置
00446D23 | mov dword ptr ds:[ebx+308],28E | [ebx+308] = 28E
00446D2D | mov dword ptr ds:[ebx+30C],9 | [ebx+30C] = 9
00446D37 | mov dword ptr ds:[ebx+314],B | [ebx+314] = B
00446D41 | xor eax,eax |
00446D43 | mov dword ptr ds:[ebx+318],eax | [ebx+318] = 0
00446D49 | mov edx,ckme002_dump_scy.446DEC | 446DEC:"X:\\ajj.126.c0m\\j\\o\\j\\o\\ok.txt"
00446D4E | lea eax,dword ptr ss:[ebp-1D0] |
00446D54 | call @Assign |
00446D59 | lea eax,dword ptr ss:[ebp-1D0] |
00446D5F | call @ResetText |
00446D64 | call IOResult |
00446D69 | test eax,eax | 如果成功读取文件,eax=0
00446D6B | jne ckme002_dump_scy.446DB8 |
00446D6D | lea edx,dword ptr ss:[ebp-4] |
00446D70 | lea eax,dword ptr ss:[ebp-1D0] |
00446D76 | call @ReadString |
00446D7B | call @_IOTest | 读取文件
00446D80 | mov eax,dword ptr ss:[ebp-4] |
00446D83 | mov edx,ckme002_dump_scy.446E14 | ’ ajj写的CKme真烂!ÿÿ‘
00446D88 | call @LStrCmp |
00446D8D | je ckme002_dump_scy.446D99 |
00446D8F | mov dword ptr ds:[ebx+304],C34 |
00446D99 | lea eax,dword ptr ss:[ebp-1D0] |
00446D9F | call @Close |
00446DA4 | call @_IOTest |
00446DA9 | mov dl,1 |
00446DAB | mov eax,dword ptr ds:[ebx+2F0] | TForm.Edit2
00446DB1 | call TControl.SetVisible | 通过后,显示输入框2
00446DB6 | jmp ckme002_dump_scy.446DC2 |
00446DB8 | mov dword ptr ds:[ebx+304],C34 |
00446DC2 | xor eax,eax |
如果对应文件路径及内容正确,[ebx+304] 为 0,为了方便,我将硬编码的路径盘符换了一下,重新运行后,能够看到第二个输入框,但是无法输入,继续到OnMOuseMove函数
TControl 事件 - C++ Builder 参考手册 - C++ 爱好者
当鼠标在控件里面移动的时候会产生 OnMouseMove 事件,这个事件响应 Windows 消息:WM_MOUSEMOVE。请参考 MouseMove 方法。参数 X 和 Y 为鼠标的位置。
typedef void __fastcall (__closure *TMouseMoveEvent)( System::TObject* Sender, System::Classes::TShiftState Shift, int X, int Y);
004470EC | push ebp |
004470ED | mov ebp,esp |
......
0044710A | mov ecx,dword ptr ds:[ebx+2E0] | TForm1.Image3
00447110 | cmp byte ptr ds:[ecx+47],1 | TImage.FVisible
00447114 | jne ckme002_dump_scy.44712F | 如果图片三可见
00447116 | cmp eax,E2 |
0044711B | jle ckme002_dump_scy.44712F | 鼠标的X坐标 <= E2
0044711D | cmp edx,12C |
00447123 | jle ckme002_dump_scy.44712F | 鼠标的Y坐标 <= 12C
00447125 | mov dword ptr ds:[ebx+310],10 | [ebx+310] = 10
0044712F | mov ecx,dword ptr ds:[ebx+2DC] | TForm1.Image2
00447135 | cmp byte ptr ds:[ecx+47],1 | TImage.FVisible
00447139 | jne ckme002_dump_scy.4471A7 | 如果图片二可见
0044713B | cmp eax,17 |
0044713E | jge ckme002_dump_scy.4471A7 | 鼠标的X坐标 >= 17
00447140 | cmp edx,12C |
00447146 | jle ckme002_dump_scy.4471A7 | 鼠标的Y坐标 <= 12C
00447148 | cmp dword ptr ds:[ebx+310],10 | 如果图片三的判断通过
0044714F | jne ckme002_dump_scy.4471A7 |
00447151 | cmp dword ptr ds:[ebx+30C],9 | [ebx+30C] == 9
00447158 | je ckme002_dump_scy.4471A7 |
0044715A | mov dword ptr ds:[ebx+310],F94 | [ebx+310] = F94
00447164 | mov eax,dword ptr ds:[ebx+30C] |
0044716A | sub eax,1 |
0044716D | jb ckme002_dump_scy.447179 | [ebx+30C] < 9
0044716F | je ckme002_dump_scy.447185 | [ebx+30C] == 9 不会执行
00447171 | dec eax | eax--
00447172 | je ckme002_dump_scy.447191 |
00447174 | dec eax | eax--
00447175 | je ckme002_dump_scy.44719D |
00447177 | jmp ckme002_dump_scy.4471A7 |
00447179 | mov dword ptr ds:[ebx+314],41 | [ebx+314] = 41
00447183 | jmp ckme002_dump_scy.4471A7 |
00447185 | mov dword ptr ds:[ebx+314],3D | [ebx+314] = 3D
0044718F | jmp ckme002_dump_scy.4471A7 |
00447191 | mov dword ptr ds:[ebx+314],34 | [ebx+314] = 34
0044719B | jmp ckme002_dump_scy.4471A7 |
0044719D | mov dword ptr ds:[ebx+314],DF | [ebx+314] = DF
004471A7 | cmp dword ptr ds:[ebx+310],F94 | [ebx+310] == F94
004471B1 | jne ckme002_dump_scy.4471F9 |
004471B3 | lea edx,dword ptr ss:[ebp-4] |
004471B6 | mov eax,dword ptr ds:[ebx+2E8] | TForm1.Edit1
004471BC | call TControl.GetText |
004471C1 | mov eax,dword ptr ss:[ebp-4] |
004471C4 | mov edx,ckme002_dump_scy.447230 | 447230:"ajj"
004471C9 | call @LStrCmp |
004471CE | jne ckme002_dump_scy.4471F9 |
004471D0 | mov dl,1 |
004471D2 | mov eax,dword ptr ds:[ebx+2FC] | TForm1.Label3
004471D8 | call ckme002_dump_scy.423FA4 | TControl.SetVisible
004471DD | lea edx,dword ptr ss:[ebp-8] |
004471E0 | mov eax,dword ptr ds:[ebx+30C] |
004471E6 | call IntToStr |
004471EB | mov edx,dword ptr ss:[ebp-8] |
004471EE | mov eax,dword ptr ds:[ebx+2FC] | TForm1.Label3
004471F4 | call TControl.SetText |
......
读完后主要有两个关键可控值,[ebx+30C],鼠标坐标,为了保证按照预期进行,需要满足条件
Mouse.Y >= 12C && [ebx+30C] != 9
if Image3.FVisible{
Mouse.X >= E2
}
if Image3.FVisible{
MOuse.X <= 17
}
因为在FormCreate后,[ebx+30C]被置为9,不满足,所以需要别的操作触发修改值,在Edit2的OnDbleClock处有修改该位置的代码
下断后发现不触发,应该是之前在Create时Edit2被禁用,所以不触发,再找启用Edit2的触发位置,为Panel1的OnDblClick
00446FDC | cmp dword ptr ds:[eax+308],29D | [eax+308] == 29D
00446FE6 | jne ckme002_dump_scy.446FF5 |
00446FE8 | mov dl,1 |
00446FEA | mov eax,dword ptr ds:[eax+2F0] | TForm1.Edit2
00446FF0 | mov ecx,dword ptr ds:[eax] |
00446FF2 | call dword ptr ds:[ecx+5C] | TControl.SetEnabled
00446FF5 | ret |
再去寻找赋值[eax+308]的位置,会在Button1的MouseDown看到
TControl 事件 - C++ Builder 参考手册 - C++ 爱好者
当鼠标按钮点击了控件会产生这个事件。这个事件响应 Windows 消息:WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN。请参考 MouseDown 方法。 参数 Button 为点击的鼠标按钮,Shift 为组合键的状态,X 和 Y 为鼠标位置
typedef void __fastcall (__closure *TMouseEvent)( System::TObject* Sender, System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, int X, int Y);
这里是fastcall所以传入的Button存在ecx
00446FA4 | push ebp |
00446FA5 | mov ebp,esp |
00446FA7 | mov edx,dword ptr ds:[eax+308] | 第一次点击,为初始值28E
00446FAD | cmp edx,230D |
00446FB3 | je ckme002_dump_scy.446FD5 |
00446FB5 | cmp cl,1 | 左键点击,ecx为0,右键为1
00446FB8 | jne ckme002_dump_scy.446FC3 |
00446FBA | add dword ptr ds:[eax+308],3 | 如果右键,[eax+308] += 3
00446FC1 | jmp ckme002_dump_scy.446FD5 |
00446FC3 | cmp edx,294 | 如果左键,去和294做cmp
00446FC9 | jge ckme002_dump_scy.446FD5 | [eax+308] >= 294
00446FCB | mov dword ptr ds:[eax+308],230D | 满足条件不赋值
00446FD5 | pop ebp |
00446FD6 | ret C |
所以需要点击Button1正确赋值[eax+308]为29D后,才能进行DblClick动作
(29D-28E)/3=5 右键点击注册5次后,点不点左键都可以,但是不能在右键五次之前点击左键
成功启用Edit2后,继续触发DblClick来更改[ebx+30C]的值,使00447158处的代码按预期继续执行。
00446FF8 | push ebp |
......
00447013 | lea edx,dword ptr ss:[ebp-4] |
00447016 | mov eax,dword ptr ds:[ebx+2F0] | TForm1.Edit2
0044701C | call TControl.GetText |
00447021 | mov eax,dword ptr ss:[ebp-4] |
00447024 | call @LStrLen |
00447029 | cmp eax,8 | len(Edit2) == 8
0044702C | jne ckme002_dump_scy.4470C4 |
00447032 | lea edx,dword ptr ss:[ebp-8] |
00447035 | mov eax,dword ptr ds:[ebx+2F0] | TForm1.Edit2
0044703B | call TControl.GetText |
00447040 | mov eax,dword ptr ss:[ebp-8] |
00447043 | cmp byte ptr ds:[eax+1],5F | Edit2[1] == '_'
00447047 | jne ckme002_dump_scy.4470C4 |
00447049 | lea edx,dword ptr ss:[ebp-C] |
0044704C | mov eax,dword ptr ds:[ebx+2F0] | TForm1.Edit2
00447052 | call TControl.GetText |
00447057 | mov eax,dword ptr ss:[ebp-C] |
0044705A | cmp byte ptr ds:[eax+5],2C | Edit2[5] == ','
0044705E | jne ckme002_dump_scy.4470C4 |
00447060 | lea edx,dword ptr ss:[ebp-10] |
00447063 | mov eax,dword ptr ds:[ebx+2E8] | TForm1.Edit1
00447069 | call TControl.GetText |
0044706E | mov eax,dword ptr ss:[ebp-10] |
00447071 | call @LStrLen |
00447076 | add eax,3 | len(Edit1) += 3
00447079 | mov ecx,3 |
0044707E | cdq |
0044707F | idiv ecx | (Edit1+3) % 3
00447081 | test edx,edx |
00447083 | jne ckme002_dump_scy.4470C4 |
00447085 | push 0 |
00447087 | push 4 |
00447089 | lea edx,dword ptr ss:[ebp-14] |
0044708C | mov eax,dword ptr ds:[ebx+2E8] | TForm1.Edit1
00447092 | call TControl.GetText |
00447097 | mov eax,dword ptr ss:[ebp-14] |
0044709A | call @LStrLen |
0044709F | cdq |
004470A0 | push edx |
004470A1 | push eax |
004470A2 | xor eax,eax |
004470A4 | call DiskFree |
004470A9 | add eax,dword ptr ss:[esp] |
004470AC | adc edx,dword ptr ss:[esp+4] |
004470B0 | add esp,8 |
004470B3 | add eax,2 |
004470B6 | adc edx,0 |
004470B9 | call @_llmod |
004470BE | mov dword ptr ds:[ebx+30C],eax | [ebx+30C] = 1 || 0
......
004470EA | ret
当Edit2的长度为8,第二位是”_",第6位为",“且Edit1长度能被3整除时,[ebx+30C]会被赋值为1或0。
当我输入长度为3的倍数的名字时,[ebx+30C]会被赋值为0,Label3不会显示
具体原因得看下@_llmod的返回定义,
由于赋值为0,在0044716D处的判断赋值也会发生变化,[ebx+314]赋值为41,
之后点击图片的次数也需要重新计算,不过逻辑相同
回到OnMouseMove,按照要求,用户名输入ajj,第三张图片时,右下角移动鼠标,然后第二张图片,左下角移动鼠标,按照预期执行。[ebx+310]被赋值为0F94,[ebx+314]被赋值为3D,Label3显示[ebx+30C],即1
此时验证判断逻辑(Timer2)按照预期走到了00447403,之后查找[ebx+318],[ebx+314],[ebx+31C]被修改的位置,发现在Image的OnMouseDown中修改了[ebx+318]
00447378 | push ebp |
00447379 | mov ebp,esp |
0044737B | push ebx |
0044737C | push esi |
0044737D | mov ebx,ecx |
0044737F | mov esi,eax |
00447381 | push 0 |
00447383 | mov cx,word ptr ds:[4473B4] |
0044738A | mov dl,2 |
0044738C | mov eax,ckme002_dump_scy.4473C0 | '注册尚未成功'
00447391 | call MessageDlg |
00447396 | test bl,bl |
00447398 | jne ckme002_dump_scy.4473A1 |
0044739A | add dword ptr ds:[esi+318],7 | 左键,[esi+318] + 7
004473A1 | cmp bl,1 |
004473A4 | jne ckme002_dump_scy.4473AD |
004473A6 | add dword ptr ds:[esi+318],1B | 右键,[esi+318] + 1B
004473AD | pop esi |
004473AE | pop ebx |
004473AF | pop ebp |
004473B0 | ret C |
四个图片逻辑一样,加的值大小不同,但是在图片2和图片3因为有OnMouseMove动作,所以不能够使用,只能选图片1或者图片4点击,当用户名长度为3时,图片四左键1次,右键两次,用户名>3时,图片1右键3次,图片4左键2次,之后[ebx+31C]的赋值,在右键Button部分就同时触发完成了
控件的点击事件,控件用鼠标点击,或者键盘的空格或回车键,还有快捷键等,都可能会产生这个事件,请参考 Click 方法。
https://www.cppfans.com/cbknowledge/reference/vcl.baseclasses/tcontrol_events.asp#OnClick
所以没有注册机,和输入有关的两个条件是,Edit2的长度,第一位,第六位,Edit1的长度,剩下的就是操作
1.创建文件X:\\ajj.126.c0m\\j\\o\\j\\o\\ok.txt 内容为指定内容
2.右键注册按钮五次
3.点击图片窗口空白处
4.Edit1输入长度为3x,若输入ajj,会弹出Lable3,Edit2输入x_xxx,xx,双击Edit2
5.第三张图片显示时,右下角移入鼠标
6.第二张图片显示时,左下角移入鼠标
7.点击第四张图片 右键两次,左键一次,如果长度>3,图4左键2次,图1右键三次
aLoNg3x.1#
目的是移除底部俩个按钮
IDR结合ida看下,在两个对应按钮键位置就可以实现
Cancella按钮
00442EA8 | push ebp |
00442EA9 | mov ebp,esp |
......
00442EBE | lea edx,dword ptr ss:[ebp-4] |
00442EC1 | mov eax,dword ptr ds:[ebx+2E0] | TPrincpale.Codice
00442EC7 | call TConTrol.GetText |
00442ECC | mov eax,dword ptr ss:[ebp-4] |
00442ECF | call StrToInt |
00442ED4 | push eax |
00442ED5 | lea edx,dword ptr ss:[ebp-4] |
00442ED8 | mov eax,dword ptr ds:[ebx+2DC] | TPincpale.Nome
00442EDE | call TConTrol.GetText |
00442EE3 | mov eax,dword ptr ss:[ebp-4] | eax=Nome
00442EE6 | pop edx | edx=StrToInt(Codice)
00442EE7 | call along3x.1.442AF4 | 该位置为关键call
00442EEC | test al,al |
00442EEE | je along3x.1.442F0C |
00442EF0 | xor edx,edx |
00442EF2 | mov eax,dword ptr ds:[ebx+2D0] | TPrincipale.Cancella
00442EF8 | call TControl.SetVisible | 设置Cancella按钮不可见
00442EFD | mov dl,1 |
00442EFF | mov eax,dword ptr ds:[ebx+2CC] | TPrincpale.Ok
00442F05 | mov ecx,dword ptr ds:[eax] |
00442F07 | call TControl.SetEnabled | 启用OK按钮
00442F0A | jmp along3x.1.442F1C |
00442F0C | mov edx,along3x.1.442F48 | 关键位置失败跳转,
00442F11 | mov eax,dword ptr ds:[ebx+2E0] | TPincpale.Codice
00442F17 | call TControl.SetText | Codice置0
......
00442F3C | ret |
OK按钮
00442D64 | push ebp |
...... 传入的参数和另一个按钮一样.Codice和Nome,call的函数不同,不重复解释
00442DC1 | call along3x.1.442BA0 |
00442DC6 | test al,al |
00442DC8 | je along3x.1.442DD7 |
00442DCA | xor edx,edx |
00442DCC | mov eax,dword ptr ds:[ebx+2CC] | TPrincipale.Ok
00442DD2 | call TConTrol.SetEnabled | 设置Ok不可见
......
00442DF7 | ret |
还有一个OK按钮的Enable问题,在Codice和Nome的OnChange事件里,同时如果Cancella按钮不可见,Ok键也会直接启用
Nome.OnChange(Codice.OnChange基本一样,不重复解释)
00442E04 | push ebp |
......
00442E1C | mov eax,dword ptr ds:[ebx+2D0] | TPrincipale.Cancella
00442E22 | cmp byte ptr ds:[eax+47],0 | TButton.FVisible
00442E26 | jne along3x.1.442E37 | Cancella按钮是否可见
00442E28 | mov dl,1 |
00442E2A | mov eax,dword ptr ds:[ebx+2CC] | TPrincipale.Ok
00442E30 | mov ecx,dword ptr ds:[eax] |
00442E32 | call TControl.SetEnabled | 如果不可见,Enable Ok按钮
00442E35 | jmp along3x.1.442E80 |
00442E37 | lea edx,dword ptr ss:[ebp-4] |
00442E3A | mov eax,dword ptr ds:[ebx+2E0] | TPincpale.Codice
00442E40 | call TConTrol.GetText |
00442E45 | mov eax,dword ptr ss:[ebp-4] |
00442E48 | push eax |
00442E49 | lea edx,dword ptr ss:[ebp-8] |
00442E4C | mov eax,dword ptr ds:[ebx+2DC] | TPincpale.Nome
00442E52 | call TConTrol.GetText |
00442E57 | mov eax,dword ptr ss:[ebp-8] |
00442E5A | pop edx |
00442E5B | call along3x.1.442A3C | 关键call 是否启用OK
00442E60 | test al,al |
00442E62 | je along3x.1.442E73 |
00442E64 | mov dl,1 |
00442E66 | mov eax,dword ptr ds:[ebx+2CC] | TPrincipale.Ok
00442E6C | mov ecx,dword ptr ds:[eax] |
00442E6E | call TConTrol.SetEnabled |
00442E71 | jmp along3x.1.442E80 |
00442E73 | xor edx,edx |
00442E75 | mov eax,dword ptr ds:[ebx+2CC] | TPrincipale.Ok
00442E7B | mov ecx,dword ptr ds:[eax] |
00442E7D | call TConTrol.SetEnabled |
......
00442EA6 | ret |
00442C78 | push ebp |
...... 调用和传参逻辑和NomeOnChange相似,不重复解释
00442D0B | call along3x.1.442A3C | 关键call 是否启用OK
00442D10 | test al,al |
....... 后续也一样,如果返回非0,启用Ok,否则不启用
00442D62 | ret |
后续关注关键位置函数传参分别为
sub_00442AF4(*Nome,StrToInt(Codice))
sub_00442BA0(*Nome,*Codice)
00442AF4
00442AF4 | push ebp |
00442AF5 | mov ebp,esp |
00442AF7 | add esp,FFFFFFF8 |
00442AFA | push ebx |
00442AFB | push esi |
00442AFC | mov dword ptr ss:[ebp-8],edx | Codice
00442AFF | mov dword ptr ss:[ebp-4],eax | Nome
......
00442B18 | mov eax,dword ptr ss:[ebp-4] | Nome
00442B1B | call @LStrLen |
00442B20 | cmp eax,5 | Nome长度大于5
00442B23 | jle along3x.1.442B78 |
00442B25 | mov eax,dword ptr ss:[ebp-4] | Nome
00442B28 | movzx eax,byte ptr ds:[eax+4] | Nome[4]
00442B2C | mov ecx,7 |
00442B31 | xor edx,edx |
00442B33 | div ecx | Nome[4] % 7
00442B35 | mov eax,edx |
00442B37 | add eax,2 | Nome[4] % 7 + 2
00442B3A | call along3x.1.442A20 | (Nome[4] % 7 + 2)!
00442B3F | mov esi,eax |
00442B41 | xor ebx,ebx |
00442B43 | mov eax,dword ptr ss:[ebp-4] | Nome
00442B46 | call @LStrLen | eax = LStrLen(Nome)
00442B4B | test eax,eax |
00442B4D | jle along3x.1.442B65 |
00442B4F | mov edx,1 |
00442B54 | mov ecx,dword ptr ss:[ebp-4] | *Nome[i]
00442B57 | movzx ecx,byte ptr ds:[ecx+edx-1] |
00442B5C | imul ecx,esi |
00442B5F | add ebx,ecx | ebx += 阶乘*Nome[i]
00442B61 | inc edx |
00442B62 | dec eax | eax--
00442B63 | jne along3x.1.442B54 |
00442B65 | sub ebx,dword ptr ss:[ebp-8] | ebx = ebx-Codice
00442B68 | cmp ebx,7A69 | ebx == 7A69
00442B6E | jne along3x.1.442B74 |
00442B70 | mov bl,1 | 等于 bl =1
00442B72 | jmp along3x.1.442B7A |
00442B74 | xor ebx,ebx | 不等于 ebx = 0
00442B76 | jmp along3x.1.442B7A |
......
00442B9E | ret | return ebx
另一个关键函数
00442BA0 | push ebp |
00442BA1 | mov ebp,esp |
00442BA3 | push 0 |
00442BA5 | push 0 |
00442BA7 | push 0 |
00442BA9 | push ebx |
00442BAA | push esi |
00442BAB | mov esi,edx | int(Codice)
00442BAD | mov dword ptr ss:[ebp-4],eax | Nome
......
00442BC6 | xor ebx,ebx |
00442BC8 | lea edx,dword ptr ss:[ebp-8] |
00442BCB | mov eax,esi | int(Codice)
00442BCD | call IntToStr |
00442BD2 | lea eax,dword ptr ss:[ebp-C] |
00442BD5 | mov edx,dword ptr ss:[ebp-8] | str(Codice)
00442BD8 | call @LStrLAsg | 复制了一份到 [ebp-C]
00442BDD | mov eax,dword ptr ss:[ebp-8] | str(Codice)
00442BE0 | call @LStrLen |
00442BE5 | cmp eax,5 | len(str(Codice)) > 5?
00442BE8 | jle along3x.1.442C4A |
00442BEA | mov eax,dword ptr ss:[ebp-8] | str(Codice)
00442BED | call @LStrLen |
00442BF2 | mov esi,eax | esi = len(Codice)
00442BF4 | cmp esi,1 |
00442BF7 | jl along3x.1.442C28 |
00442BF9 | lea eax,dword ptr ss:[ebp-C] | str(Codice)
00442BFC | call UniqueString | [ebp-C] = str(Codice)
00442C01 | lea eax,dword ptr ds:[eax+esi-1] | *[ebp-C][esi-1]
00442C05 | push eax |
00442C06 | mov eax,dword ptr ss:[ebp-8] | str(Codice)
00442C09 | movzx eax,byte ptr ds:[eax+esi-1] | Codice[esi-1]
00442C0E | imul eax | Codice[esi-1]平方
00442C10 | movsx eax,ax |
00442C13 | imul esi | Codice[esi-1]平方*esi
00442C15 | mov ecx,19 | Codice[esi-1]平方*esi%19
00442C1A | cdq |
00442C1B | idiv ecx |
00442C1D | add edx,41 | Codice[esi-1]平方*esi%19+41
00442C20 | pop eax |
00442C21 | mov byte ptr ds:[eax],dl | *[ebp-C][esi-1] = char(Codice[esi-1]平方*esi%19+41)
00442C23 | dec esi | esi--
00442C24 | test esi,esi |
00442C26 | jne along3x.1.442BF9 |
00442C28 | mov eax,dword ptr ss:[ebp-C] | 转换后的码
00442C2B | mov edx,dword ptr ss:[ebp-4] | Nome
00442C2E | call StrCmp |
00442C33 | jne along3x.1.442C4C |
00442C35 | mov eax,dword ptr ss:[ebp-4] | Nome
00442C38 | mov edx,dword ptr ss:[ebp-C] | 转换后的码
00442C3B | call StrCmp |
00442C40 | jne along3x.1.442C46 |
00442C42 | mov bl,1 | 相等 return 1
00442C44 | jmp along3x.1.442C4C |
00442C46 | xor ebx,ebx |
00442C48 | jmp along3x.1.442C4C |
........
00442C75 | ret |
可能纯看比较乱,逻辑就是计算输入的Codice,调试下比较方便
package main
import (
"fmt"
)
func factorial(x int) int {
if x > 0 {
return x * factorial(x-1)
} else {
return 1
}
}
func GetNome(codice string) {
if len(codice) > 5 {
// 转换为字节数组
CodiceBytes := []byte(codice)
for i := len(CodiceBytes); i > 0; i-- {
hexValue := int(CodiceBytes[i-1])
tmp := (hexValue*hexValue*i)%0x19 + 0x41
character := rune(tmp)
CodiceBytes[i-1] = byte(character)
}
// 将字节数组转换回字符串
Nome := string(CodiceBytes)
fmt.Printf("Ok按钮组合\n\tNome:%s\n\tCodice:%s\n", Nome, codice)
}
}
func GetCodice(nome string) {
if len(nome) > 5 {
sum := 0
for i := 0; i < len(nome); i++ {
sum += int(nome[i]) * factorial(int(nome[4])%7+2)
}
Codice := sum - 31337
fmt.Printf("Cancella按钮组合\n\tNome:%s\n\tCodice:%d\n", nome, Codice)
}
}
func main() {
nome := "Dawn12"
codice := "123456"
GetNome(codice)
GetCodice(nome)
}
aLoNg3x.2#
目的和.1一样,和之前操作一样拉入IDR,ida和dbg
先查看三个按钮的事件,大致逻辑是,Register成功后,Register会消失,出现Again按钮,解决Again达成目的
Register按钮,成功后Nome会被锁定
00442F28 | push ebp |
......
00442F45 | lea edx,dword ptr ss:[ebp-8] |
00442F48 | mov eax,dword ptr ds:[ebx+2DC] | TPincpale.Codice
00442F4E | call TControl.GetText |
00442F53 | mov eax,dword ptr ss:[ebp-8] |
00442F56 | lea edx,dword ptr ss:[ebp-4] |
00442F59 | call @ValLong | hex(Codice)
00442F5E | mov esi,eax |
00442F60 | cmp dword ptr ss:[ebp-4],0 |
00442F64 | je along3x.2.442F9D |
00442F66 | mov eax,along3x.2.443038 | 443038:"You MUST insert a valid Long Integer Value in the Code Editor... Thank you :)"
00442F6B | call ShowMessage |
00442F70 | lea edx,dword ptr ss:[ebp-8] |
00442F73 | mov eax,dword ptr ds:[ebx+2DC] | TPincpale.Codice
00442F79 | call TControl.GetText |
00442F7E | mov eax,dword ptr ss:[ebp-8] |
00442F81 | call TWindowDesigner::SelectAll(void) |
00442F86 | mov dword ptr ds:[445830],eax | 这里有一次赋值,必须要返回1
00442F8B | mov edx,along3x.2.443090 | 0
00442F90 | mov eax,dword ptr ds:[ebx+2DC] | TPincpale.Codice
00442F96 | call TControl.SetText |
00442F9B | jmp along3x.2.44300C |
00442F9D | test esi,esi |
00442F9F | jle along3x.2.442FFB |
00442FA1 | lea edx,dword ptr ss:[ebp-8] |
00442FA4 | mov eax,dword ptr ds:[ebx+2D8] | TPincpale.Nome
00442FAA | call TControl.GetText |
00442FAF | mov ecx,dword ptr ss:[ebp-8] | Nome
00442FB2 | mov edx,esi | ValLong(Codice)
00442FB4 | mov eax,dword ptr ds:[445830] | BSS
00442FB9 | call along3x.2.4429A8 | 关键位置
00442FBE | test al,al |
00442FC0 | je along3x.2.442FF2 |
00442FC2 | xor edx,edx |
00442FC4 | mov eax,dword ptr ds:[ebx+2CC] | TPincpale.Registerz
00442FCA | call TControl.SetVisible |
00442FCF | mov dl,1 |
00442FD1 | mov eax,dword ptr ds:[ebx+2E8] | TPoncpale.Again
00442FD7 | call TControl.SetVisible |
00442FDC | xor edx,edx |
00442FDE | mov eax,dword ptr ds:[ebx+2D8] | TPincpale.Nome
00442FE4 | mov ecx,dword ptr ds:[eax] |
00442FE6 | call TControl.SetEnabled |
00442FE9 | xor eax,eax |
00442FEB | mov dword ptr ds:[445830],eax |
00442FF0 | jmp along3x.2.44300C |
......
0044302E | ret |
关键位置函数
004429A8 | push ebp |
......
004429B1 | mov dword ptr ss:[ebp-8],ecx | Nome
004429B4 | mov dword ptr ss:[ebp-4],edx | ValLong(Codice)
004429B7 | mov edi,eax | [00445830]
......
004429CF | mov eax,dword ptr ss:[ebp-8] | Nome
004429D2 | call @LStrLen |
004429D7 | cmp eax,4 | 长度大于4
004429DA | jle along3x.2.442A62 |
004429E0 | xor ebx,ebx |
004429E2 | mov eax,dword ptr ss:[ebp-8] |
004429E5 | call @LStrLen |
004429EA | test eax,eax |
004429EC | jle along3x.2.442A26 |
004429EE | mov dword ptr ss:[ebp-C],eax | [ebp-C] = len(Nome)
004429F1 | mov esi,1 |
004429F6 | mov eax,dword ptr ss:[ebp-8] |
004429F9 | call @LStrLen | eax = len(Nome)
004429FE | cmp eax,1 |
00442A01 | jl along3x.2.442A20 |
00442A03 | mov edx,dword ptr ss:[ebp-8] |
00442A06 | movzx edx,byte ptr ds:[edx+esi-1] | Nome[esi-1]
00442A0B | mov ecx,dword ptr ss:[ebp-8] |
00442A0E | movzx ecx,byte ptr ds:[ecx+eax-1] | Nome[eax-1]
00442A13 | imul edx,ecx | edx = ecx*edx
00442A16 | imul edx,edi | edx = edx*[00445830]
00442A19 | add ebx,edx | ebx += edx
00442A1B | dec eax | eax--
00442A1C | test eax,eax |
00442A1E | jne along3x.2.442A03 |
00442A20 | inc esi | esi++
00442A21 | dec dword ptr ss:[ebp-C] |
00442A24 | jne along3x.2.4429F6 |
00442A26 | mov eax,ebx |
00442A28 | cdq |
00442A29 | xor eax,edx |
00442A2B | sub eax,edx | abs32(eax)
00442A2D | mov ecx,A2C2A |
00442A32 | cdq |
00442A33 | idiv ecx |
00442A35 | mov ebx,edx | ebx = eax%A2C2A
00442A37 | mov eax,dword ptr ss:[ebp-4] | ValLong(Codice)
00442A3A | mov ecx,59 |
00442A3F | cdq |
00442A40 | idiv ecx | ValLong(Codice)/59
00442A42 | mov ecx,eax |
00442A44 | mov eax,dword ptr ss:[ebp-4] |
00442A47 | mov esi,50 |
00442A4C | cdq |
00442A4D | idiv esi |
00442A4F | add ecx,edx | +ValLong(Codice)%59
00442A51 | inc ecx | ++
00442A52 | mov dword ptr ss:[ebp-4],ecx |
00442A55 | cmp ebx,dword ptr ss:[ebp-4] | ecx==ebx
......
00442A89 | ret |
可以结合ida看下
先要使Codice小于1,成功触发一次SelectAll(00442A8C),使函数返回非0赋值给[00445830],计算才能成功进行,否则求和的ebx始终为0,无法与计算的ecx相等,没有在网上找到这个SelectAll的信息,去调试和静态看下,逻辑和刚才分析的函数差不多,也是遍历计算
看了一下Again按钮的操作似乎和Register完全一样,调用的函数也相同,操作了一下,真成了,好像真的是Again,没再多注意了,至于Cancella按钮应该是无用的,似乎会弹框"Great”,但是目标是清除按钮,也没做研究了。
注册机
package main
import (
"fmt"
)
func main() {
FakeCodie := "Dawn123" //第一次输入用于触发赋值及弹窗的的内容
Nome := "Dawn123"
if len(FakeCodie) < 5 {
print("FakeCodie 长度大于5!")
} else {
val := TestNome(FakeCodie)
ebx := GetEbx(val, Nome)
Codie := GetX(ebx)
fmt.Printf("FakeCodie: %s,Nome: %s,Codice: %d\n", FakeCodie, Nome, Codie)
}
}
//计算赋值[00445830]的值(00442A8C)
func TestNome(Nome string) int {
sum := 891
for i := 1; i < len(Nome); i++ {
sum += int(Nome[i-1]) * (int(Nome[i])%0x11 + 1)
}
if sum%29000 != 0 {
fmt.Printf("FakeCodie合法\n")
return sum % 29000
} else {
print("Wrong")
return 0
}
}
//计算根据Nome的值(004429A8)
func GetEbx(val int, nome string) int {
sum := 0
for i := 0; i < len(nome); i++ {
for j := len(nome); j > 0; j-- {
sum += val * int(nome[i]) * int(nome[j-1])
}
}
return sum % 666666
}
//逆运算解方程,求出输入的Codice
func GetX(n int) (res int) {
for x := 0; ; x++ {
if (x%80 + x/89 + 1) == n {
return x
}
}
}
Andrénalin.1#
VB5程序,拉入VBDecompliner
似乎就一个事件,直接是字符串比较,输入就过了
Andrénalin.2#
这个有两个事件,一个是Text2_Change,也就是Name,主要功能是 Name必须不能为空,为空不能点OK
然后直接是验证,先看了VBDec的伪代码,逻辑是
for i:=0;i<len(Name);i++{
sum+=Name[i]
}
sum *= 1234567890
str(sum)[8] = "-"
但是计算输进去不对,还是需要自己看下,但是VB他的调用传参应该是有特殊的数据结构,入参的指针感觉都是
数据类型(4字节)+不知道是什么(有的是长度,有的是地址)+值(地址或值,看数据类型)
所以基本数据的位置都在+8的位置,并且调用的返回也不太搞得懂,比如下面For循环里的调用就搞不太懂具体数据的传递逻辑,会在某个点跟丢,只能根据结果推,如果有类似函数传参或者数据类型结构的参考的文档期待分享
00401FF0 | push ebp |
......
00402132 | test eax,eax | For循环
...... 逻辑就是把Name的Ascii加起来
004021CB | call dword ptr ds:[<&__vbaVarForNext>] |
004021D1 | jmp andrénalin.2.402132 | For结束
004021D6 | lea ecx,dword ptr ss:[ebp-34] | 加法结果
004021D9 | lea edx,dword ptr ss:[ebp-AC] | 乘数
004021DF | push ecx |
004021E0 | lea eax,dword ptr ss:[ebp-6C] |
004021E3 | push edx |
004021E4 | push eax |
004021E5 | mov dword ptr ss:[ebp-A4],499602D2 | 乘数指针位置+8 赋值为1234567890
004021EF | mov dword ptr ss:[ebp-AC],3 | 乘数指针数据类型赋值3,应该是long?
004021F9 | call dword ptr ds:[<&__vbaVarMul>] |
004021FF | mov edx,eax | 这里比较疑惑,返回的结果我看eax并不是乘法的结果,也不是指针,跟不到
00402201 | lea ecx,dword ptr ss:[ebp-34] |
00402204 | call VarMove | 乘法结果eax 存在[ebp-34]
00402206 | mov ebx,dword ptr ds:[<&__vbaMidStmtVa | Mid函数
0040220C | lea ecx,dword ptr ss:[ebp-34] | 结果存在[ebp-34]
0040220F | push ecx |
00402210 | push 4 | 第四位
00402212 | lea edx,dword ptr ss:[ebp-AC] | 替换的字符串
00402218 | push 1 |
0040221A | push edx |
0040221B | mov dword ptr ss:[ebp-A4],andrénalin.2 | "-"
00402225 | mov dword ptr ss:[ebp-AC],8 | 应该是字符串类型
0040222F | call ebx | Mid
00402231 | lea eax,dword ptr ss:[ebp-34] |
00402234 | lea ecx,dword ptr ss:[ebp-AC] |
0040223A | push eax |
0040223B | push 9 | 第9位
0040223D | push 1 |
0040223F | push ecx |
00402240 | mov dword ptr ss:[ebp-A4],andrénalin.2 | "-"
0040224A | mov dword ptr ss:[ebp-AC],8 |
00402254 | call ebx | Mid 这里完了之后就可以在对应位置找到序列号明文
......
00402292 | mov eax,dword ptr ss:[ebp-58] | 输入的序列号
00402295 | lea ecx,dword ptr ss:[ebp-34] | 生成的序列号
00402298 | mov dword ptr ss:[ebp-64],eax |
0040229B | lea eax,dword ptr ss:[ebp-6C] |
0040229E | push eax |
0040229F | push ecx |
004022A0 | mov dword ptr ss:[ebp-58],0 |
004022A7 | mov dword ptr ss:[ebp-6C],8008 | 8008不知道什么类型
004022AE | call dword ptr ds:[<&__vbaVarTstEq>] | 字符串比较
004022B4 | lea ecx,dword ptr ss:[ebp-5C] |
004022B7 | mov ebx,eax | 比较结果
004022B9 | call dword ptr ds:[<&__vbaFreeObj>] |
004022BF | lea ecx,dword ptr ss:[ebp-6C] |
004022C2 | call dword ptr ds:[<&__vbaFreeVar>] |
004022C8 | test bx,bx | 判断
004022CB | je andrénalin.2.402391 |
004022D1 | call dword ptr ds:[<&rtcBeep>] | 声音
004022D7 | mov ebx,dword ptr ds:[<&__vbaVarDup>] | 00404198:"h\f?"
......
00402308 | mov dword ptr ss:[ebp-B4],andrénalin.2 | 标题 "RiCHTiG !"
......
00402327 | mov dword ptr ss:[ebp-A4],andrénalin.2 | 内容 " RiCHTiG !!!! .... weither"
......
00402355 | call dword ptr ds:[<&rtcMsgBox>] | 弹窗
......
0040238C | jmp andrénalin.2.402446 |
00402391 | mov ebx,dword ptr ds:[<&__vbaVarDup>] | 失败
......
004023C2 | mov dword ptr ss:[ebp-B4],andrénalin.2 | "LEiDER Falsch ! "
004023CC | mov dword ptr ss:[ebp-BC],8 |
004023D6 | call ebx |
004023D8 | lea edx,dword ptr ss:[ebp-AC] |
004023DE | lea ecx,dword ptr ss:[ebp-6C] |
004023E1 | mov dword ptr ss:[ebp-A4],andrénalin.2 | "Leider Falsch! Nochmal veruschen ! Wenn Du es nicht schaffen solltest, schreib mir ! Andrenalin@gmx.net"
......
0040240F | call dword ptr ds:[<&rtcMsgBox>] |
......
004024DF | ret 4 |
注册机也比较简单,主要困难还是在vb的汇编部分,
package main
import (
"fmt"
)
func main() {
Name := "Dawn"
sum := 0
for _, char := range Name {
sum += int(char)
}
pwd := fmt.Sprintf("%d", sum*1234567890)
pwdBytes := []byte(pwd)
pwdBytes[3] = '-'
pwdBytes[8] = '-'
pwd = string(pwdBytes)
fmt.Println(pwd)
}
Andrénalin.3#
放入VBDec只有一个事件,放入调试器
00401E20 | push ebp |
......
00401F68 | test eax,eax | 循环开始
......
00401F95 | call dword ptr ds:[<&rtcMidCharVar>] | 取字符
......
00401FA3 | call dword ptrds:[<&__vbaStrVarVal>] |
00401FA9 | push eax |
00401FAA | call dword ptr ds:[<&rtcAnsiValueBstr>] | 取字符Ascii
00401FB0 | add ax,A | int(str[i])+0xA
00401FB4 | jo andrénalin.3.40226A |
00401FBA | movsx edx,ax |
00401FBD | push edx |
00401FBE | call dword ptr ds:[<&rtcBstrFromAnsi>] | 再转换为字符串存入
......
00402020 | call dword ptr ds:[<&__vbaVarForNext>] | 循环结束
00402026 | jmp andrénalin.3.401F68 |
0040202B | lea eax,dword ptr ss:[ebp-34] |
0040202E | lea ecx,dword ptr ss:[ebp-AC] |
00402034 | push eax |
00402035 | push ecx |
00402036 | mov dword ptr ss:[ebp-A4],andrénalin.3.401A8C | "kXy^rO|*yXo*m\\kMuOn*+"
00402040 | mov dword ptr ss:[ebp-AC],8008 |
0040204A | call dword ptr ds:[<&__vbaVarTstEq>] | 循环生成的字符串和密文比较
00402050 | test ax,ax |
...... 后面都一样,相等和不相等不同弹窗
00402267 | ret 4 |
大致半猜半解吧,写了注册试了一下,成功了
package main
import (
"fmt"
)
func main() {
Name := "kXy^rO|*yXo*m\\kMuOn*+"
for _, char := range Name {
tmp := int(char) - 0xA
fmt.Printf("%c", tmp)
}
}
Andrénalin.4#
没有验证按钮,是计时器验证
找到对应验证位置,四个Timer逻辑类似,vbdec看对应位置及调试
由于调用比较麻烦,静态看的不是很清除,就通过调试验证逻辑,整体逻辑是取前1-8位的最大连续数(1-8每个有一段代码),且截断到*或#(rtcR8ValFromBstr),存后加每个字符Ascii,转16进制,前面加0,存为字符串,每个字符独立
注意下在内存窗口想看到计算值需要把数据类型改了
应该相当于这是一个有密文和算法求明文,由于这种算法出现的数都是hex,所以不需要关注其他密文里什么GKWOR之类的乱字符,算不出来这种密文,对应计算及判断也可以跳过,观察下,只有一个能够实现的计算在00406601附近的位置。
对应大致解释,其余基本一致,在push的位置不太相同
00404B46 | call dword ptr ds:[<&__vbaVarForInit>] | 循环
00404B4C | test eax,eax |
00404B4E | je andrénalin.4.404C7D |
00404B54 | lea ecx,dword ptr ss:[ebp-44] | 序列号
00404B57 | push 2 | 取2位
......
00404B6B | call dword ptr ds:[<&rtcR8ValFromBstr>] |
00404B71 | fstp qword ptr ss:[ebp-CC],st(0) | 保存
......
00404B9F | call dword ptr ds:[<&rtcMidCharBstr>] | 取一个字符
00404BA5 | mov edx,eax |
00404BA7 | lea ecx,dword ptr ss:[ebp-4C] |
00404BAA | call dword ptr ds:[<&__vbaStrMove>] |
00404BB0 | push eax |
00404BB1 | call dword ptr ds:[<&rtcAnsiValueBstr>] | 取ascii
00404BB7 | movsx eax,ax |
00404BBA | mov dword ptr ss:[ebp-310],eax |
00404BC0 | lea ecx,dword ptr ss:[ebp-84] |
00404BC6 | fild st(0),dword ptr ss:[ebp-310] | 存入st0
00404BCC | push ecx |
00404BCD | mov dword ptr ss:[ebp-84],5 |
00404BD7 | fstp qword ptr ss:[ebp-318],st(0) |
00404BDD | fld st(0),qword ptr ss:[ebp-318] |
00404BE3 | fadd st(0),qword ptr ss:[ebp-CC] | ascii+int(str[:2])
00404BE9 | fstp qword ptr ss:[ebp-7C],st(0) | 存入7C
00404BEC | fnstsw ax |
00404BEE | test al,D |
00404BF0 | jne andrénalin.4.4069CC |
00404BF6 | call dword ptr ds:[<&rtcHexBstrFromVar>] |
...... 后面就是将[ebp-7C]的值转为hex字符串前面填0存入[ebp-34]
00404C7D | lea eax,dword ptr ss:[ebp-34] |
00404C80 | lea ecx,dword ptr ss:[ebp-B4] |
00404C86 | push eax |
00404C87 | push ecx |
00404C88 | mov dword ptr ss:[ebp-AC],andrénalin.4.401EE8 | 401EE8:L"0817E747D7A7D7C7F82836D74747A7F7E7B7C7D826D817E7B7C"
00404C92 | mov dword ptr ss:[ebp-B4],8008 |
00404C9C | call dword ptr ds:[<&__vbaVarTstEq>] | 验证
对应Val只取前2位,也就对应密文一个字符最大的密文是 99+0x39=9C 是两位,所以也不存在像其他取得位数的计算结果大于2位的情况,对应把密文去除第一个0两个分组遍历计算,取得明文,我用遍历的方法,应该也够用,不想一个列对应的表了。
package main
import (
"fmt"
"strconv"
)
func main() {
// 待处理的十六进制字符串
inputHex := "817E747D7A7D7C7F82836D74747A7F7E7B7C7D826D817E7B7C"
// 将每两个字符一组的十六进制字符串转换为对应的整数并存入数组
var hexArray []int
for i := 0; i < len(inputHex); i += 2 {
chunk := inputHex[i : i+2]
hexInt, _ := strconv.ParseInt(chunk, 16, 64)
hexArray = append(hexArray, int(hexInt))
}
// 枚举可能的加密密钥,尝试解密
for key := 0; key <= 99; key++ {
var decryptedValue int
for _, cipher := range hexArray {
decryptedValue = cipher - key
// 检查解密后的值是否在可接受的字符范围内
if !(decryptedValue == 0x23 || decryptedValue == 0x2a || (decryptedValue >= 0x30 && decryptedValue <= 0x39)) {
decryptedValue = 0 // 如果存在不合法字符,则重置解密值
break
}
}
// 如果找到合适的密钥并解密成功,则打印解密后的结果并退出循环
if decryptedValue != 0 {
for _, decryptedChar := range hexArray {
fmt.Printf("%c", decryptedChar-key)
}
break
}
}
}
yptedChar-key)
}
break
}
}
}
成功注册
attackiko#
Win10没打开
badboy#
放入vbdec
和之前的VB程序不同的是,他是P-Code不是Native Code 放入ida和调试器时显示的汇编比较奇怪,看不太懂,应该是有特别的指令之类,找了下对应的WriteUp,是用反编译的执行代码复现的,看不太明白,先放下吧,P-Code的程序不太能懂
bjanes.1#
crackmes.one#
通过OutputDebugStringA进行调试器验证,如果在调试器内,则不会exception。
通过直接mov eax,1 方式更改跳转,正常运行后
由于 "H" % 64 = 8 "," % 64 = 2c
所以有两个解 [(Hex为奇数字符),44],[(Hex为偶数字符),8]