Calculate serial from input name.
한국어판 [펼치기]
여느 크랙미 처럼 이것도 GetDlgItemText 함수를 사용해서 입력값을 읽어와야..
시리얼을 확인하든 이름을 확인하든 할 것이다..
그래서 GetDlgItemText 를 사용하는 부분을 뽑아봤더니 4곳이 나왔다..
뭐 많지도 않으니 하나씩 분석을 해보자..

딱 봐도 페이크이다..
GetTickCount 함수의 결과값으로 시리얼 분기하는거 자체가 말이 안된다..
(그럼 매 마이크로초마다 시리얼이 변한다 ㅡ_ㅡ......)
아무튼 위의 연산은 아무리 지지고 볶아도 시리얼 틀렸다고 나온다..
그럼 다음 GetDlgItemText 로 넘어가 보자..

뭔가 단순하다.. (그래서 읽기도 쉽다 ㅡ_ㅡ;;)
어라? 그런데 뭔가 이상하지 않은가?
분명 dll 파일이라곤 같이 첨부되어 있지도 않은데 왠 엑스포트된 함수읽기?..
그래서 hModule을 참조하는 코드를 주욱 뒤져 봤다..
이런이런.. 알고보니 첨부된 nfo파일은 dll파일이었다..
결국 해석해 보면 advaapi32.dll 라는 파일을 만들고
info.nfo 파일의 내용을 그대로 복사한 다음 숨김속성을 넣는것이다..
(해석하기 귀찮은 사람을 위해서 C코드로 변환시킨 결과도 함께 첨부)
아무튼 이렇게 CHK라는 이상한걸 임포트 해왔으니..
생성된 DLL에서 CHK 함수의 내용을 살펴보자..

별거 없다.. 그냥 전역변수에 넘어온 변수 저장하는게 끝이다..
언제 호출 하냐고?
push offset ProcName ; "CHK"
push hModule ; hModule
call GetProcAddress
push offset byte_403200 ; 시리얼
push offset String ; 이름
call eax ; GetProcAddress가 반환한 함수의 주소
요렇게 호출한다..
그럼 키값 확인하는곳은 어디일까?
성공했어요~ 메세지를 호출하는 곳을 검색하면 젤 위의 페이크 함수 뿐이다..
그럼.. 남은 부분은?..
DLL 엔트리 함수쪽에 어태치나 디태치시 실행된다고 생각 할 수 밖에 없다..
그래서 DLL의 스트링 리스트를 뒤져보니 아니나 다를까 성공 메세지가 있다..
아무튼.. 시리얼 체크하는 루틴만 뽑아내면..

요런식으로 작동한다..
(이것도 특별히 C 번역판을 첨부해 보면..)
아무튼 체크 과정을 알았으니...
키젠 만드는건 알아서~
내가 만든 키젠의 사용법은 각 글자에 대한 2개의 문자를 아무 위치에서나 조합하면 된다..
(각 글자 하나마다 2자리의 시리얼세트가 만들어지는 구조이다..)
그리고.. 맞는 시리얼을 입력해도 일단은 틀렸다고 나온다..(코드 보면 알 것이다..)
시리얼이 맞는지의 여부는 프로그램 종료시에 알 수 있다..
이건 이 크랙미의 버그(?)이거나 함정(?)일 것이다..
시리얼을 확인하든 이름을 확인하든 할 것이다..
그래서 GetDlgItemText 를 사용하는 부분을 뽑아봤더니 4곳이 나왔다..
뭐 많지도 않으니 하나씩 분석을 해보자..

딱 봐도 페이크이다..
GetTickCount 함수의 결과값으로 시리얼 분기하는거 자체가 말이 안된다..
(그럼 매 마이크로초마다 시리얼이 변한다 ㅡ_ㅡ......)
아무튼 위의 연산은 아무리 지지고 볶아도 시리얼 틀렸다고 나온다..
그럼 다음 GetDlgItemText 로 넘어가 보자..

뭔가 단순하다.. (그래서 읽기도 쉽다 ㅡ_ㅡ;;)
어라? 그런데 뭔가 이상하지 않은가?
분명 dll 파일이라곤 같이 첨부되어 있지도 않은데 왠 엑스포트된 함수읽기?..
그래서 hModule을 참조하는 코드를 주욱 뒤져 봤다..
.text:00401282 sub_401282 proc near ; CODE XREF: sub_401337p
.text:00401282 push ebp
.text:00401283 mov ebp, esp
.text:00401285 add esp, 0FFFFFFF4h
.text:00401288 cmp hModule, 0
.text:0040128F jnz locret_401320
.text:00401295 push 200h ; nSize
.text:0040129A push offset ExistingFileName ; lpFilename
.text:0040129F push hInstance ; hModule
.text:004012A5 call GetModuleFileNameA
.text:004012AA sub eax, 0Ch
.text:004012AD mov ecx, offset ExistingFileName
.text:004012B2 add ecx, eax
.text:004012B4 mov byte ptr [ecx], 0
.text:004012B7 push offset ExistingFileName ; lpString2
.text:004012BC push offset LibFileName ; lpString1
.text:004012C1 call lstrcpyA
.text:004012C6 push offset aInfo_nfo ; "info.nfo"
.text:004012CB push offset ExistingFileName ; lpString1
.text:004012D0 call lstrcatA
.text:004012D5 push offset aAdvaapi32_dll ; "advaapi32.dll"
.text:004012DA push offset LibFileName ; lpString1
.text:004012DF call lstrcatA
.text:004012E4 push offset LibFileName ; lpFileName
.text:004012E9 call DeleteFileA
.text:004012EE push 0 ; bFailIfExists
.text:004012F0 push offset LibFileName ; lpNewFileName
.text:004012F5 push offset ExistingFileName ; lpExistingFileName
.text:004012FA call CopyFileA
.text:004012FF or eax, eax
.text:00401301 jnz short loc_401305
.text:00401303 jmp short loc_401322
.text:00401305 ; ---------------------------------------------------------------------------
.text:00401305
.text:00401305 loc_401305: ; CODE XREF: sub_401282+7Fj
.text:00401305 push 2 ; dwFileAttributes
.text:00401307 push offset LibFileName ; lpFileName
.text:0040130C call SetFileAttributesA
.text:00401311 push offset LibFileName ; lpLibFileName
.text:00401316 call LoadLibraryA
.text:0040131B mov hModule, eax
.text:00401320
.text:00401320 locret_401320: ; CODE XREF: sub_401282+Dj
.text:00401320 leave
.text:00401321 retn
.text:00401322 ; ---------------------------------------------------------------------------
.text:00401322
.text:00401322 loc_401322: ; CODE XREF: sub_401282+81j
.text:00401322 push 10h ; uType
.text:00401324 push offset aCrackme1ByVC_0 ; "CRACKME #1 BY V!CTOR - UNREGISTERED"
.text:00401329 push offset aT ; "??
.text:0040132E push 0 ; hWnd
.text:00401330 call MessageBoxA
.text:00401335 leave
.text:00401336 retn
.text:00401336 sub_401282 endp
이런이런.. 알고보니 첨부된 nfo파일은 dll파일이었다..
결국 해석해 보면 advaapi32.dll 라는 파일을 만들고
info.nfo 파일의 내용을 그대로 복사한 다음 숨김속성을 넣는것이다..
(해석하기 귀찮은 사람을 위해서 C코드로 변환시킨 결과도 함께 첨부)
void __cdecl sub_401282()
{
if ( !hModule )
{
ExistingFileName[GetModuleFileNameA(hInstance, ExistingFileName, 0x200u) - 12] = 0;
lstrcpyA(LibFileName, ExistingFileName);
lstrcatA(ExistingFileName, "info.nfo");
lstrcatA(LibFileName, "advaapi32.dll");
DeleteFileA(LibFileName);
if ( CopyFileA(ExistingFileName, LibFileName, 0) )
{
SetFileAttributesA(LibFileName, 2u);
hModule = LoadLibraryA(LibFileName);
}
else
{
MessageBoxA(0, "??, "CRACKME #1 BY V!CTOR - UNREGISTERED", 0x10u);
}
}
}
아무튼 이렇게 CHK라는 이상한걸 임포트 해왔으니..
생성된 DLL에서 CHK 함수의 내용을 살펴보자..

별거 없다.. 그냥 전역변수에 넘어온 변수 저장하는게 끝이다..
언제 호출 하냐고?
push offset ProcName ; "CHK"
push hModule ; hModule
call GetProcAddress
push offset byte_403200 ; 시리얼
push offset String ; 이름
call eax ; GetProcAddress가 반환한 함수의 주소
요렇게 호출한다..
그럼 키값 확인하는곳은 어디일까?
성공했어요~ 메세지를 호출하는 곳을 검색하면 젤 위의 페이크 함수 뿐이다..
그럼.. 남은 부분은?..
DLL 엔트리 함수쪽에 어태치나 디태치시 실행된다고 생각 할 수 밖에 없다..
그래서 DLL의 스트링 리스트를 뒤져보니 아니나 다를까 성공 메세지가 있다..
아무튼.. 시리얼 체크하는 루틴만 뽑아내면..

요런식으로 작동한다..
(이것도 특별히 C 번역판을 첨부해 보면..)
BOOL __stdcall DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
BOOL result; // eax@2
char *p2; // edi@5
char *p; // esi@5
if ( fdwReason == 1 )
{
result = 1;
}
else
{
if ( !fdwReason && buf1 )
{
p = buf1;
p2 = buf2;
while ( *p )
{
if ( (*p2 + *(p2+1)) != *p + 35 )
return MessageBoxA(0, "You are not regestered yet", "CRACKME #1 BY VCTOR", 0x10u);
++p;
p2 += 2;
}
result = MessageBoxA(
0,
"Congratulations, you are registered now!\r\n\r\n淃醴",
"CRACKME #1 BY VCTOR - REGISTERED",
0);
}
}
return result;
}
아무튼 체크 과정을 알았으니...
키젠 만드는건 알아서~
내가 만든 키젠의 사용법은 각 글자에 대한 2개의 문자를 아무 위치에서나 조합하면 된다..
(각 글자 하나마다 2자리의 시리얼세트가 만들어지는 구조이다..)
그리고.. 맞는 시리얼을 입력해도 일단은 틀렸다고 나온다..(코드 보면 알 것이다..)
시리얼이 맞는지의 여부는 프로그램 종료시에 알 수 있다..
이건 이 크랙미의 버그(?)이거나 함정(?)일 것이다..
English ver. [Show]
Likes other crackmes, It should gets input string by using GetDlgItemText functions or something, and it can checks name and serial.
So, I got 4 places where use GetDlgItemText function.
Let's check one by one..

This is first part where using a GetDlgItemText function, and obviously this is a fake routine.
Because, using a GetTickCount function for serial doesn't makes sence.
(If they do that, the serials probably changes every millisecond..)
Anyway, the codes above it always shows us that we are wrong. (Just analyze it.)
So, Let's go to the other GetDlgItemText function.

It looks very simple, so easy to understand (or not...).
But, it looks weird. This program doesn't have any DLL files, but it use export function.
Therefore, I check all references which use hModule.
No way.. attached nfo file was dll..
Anyway, try to analyze this code, you can see that it makes advaapi32.dll and copy data from info.nfo, and set hidden-file attribute.
(Here is a analysis code blow)
So, it imports one strange function(CHK)..
Let's see the function in DLL file..

There's nothing interesting. It saves arguments to global variables.
But, when it works?
push offset ProcName ; "CHK"
push hModule ; hModule
call GetProcAddress
push offset byte_403200 ; Put Serial
push offset String ; Put Name
call eax ; return function's address from GetProcAddress
But, still we don't know where it checks serial..
When I search the whole codes there is only one place which call success message.
The fake check routine.
But we have dll file also.
When DLL file attach or detach from main module, program calls entry function, and this is only one remainder place that program can check the serial.
As might have been expected, I can find success message in a DLL string list.
Anyway, pick out serial check routine below...

If convert it to C code..
Anyway, we know how to check..
Make a keygen is your work.
(Or, just use my one)
Ohh.. One more..
Even if you enter a collect serial, it'll show you that your wrong.. (maybe it's a bug or point)
So, you can know that your serial is wrong or right, when you close a program..^^
So, I got 4 places where use GetDlgItemText function.
Let's check one by one..

This is first part where using a GetDlgItemText function, and obviously this is a fake routine.
Because, using a GetTickCount function for serial doesn't makes sence.
(If they do that, the serials probably changes every millisecond..)
Anyway, the codes above it always shows us that we are wrong. (Just analyze it.)
So, Let's go to the other GetDlgItemText function.

It looks very simple, so easy to understand (or not...).
But, it looks weird. This program doesn't have any DLL files, but it use export function.
Therefore, I check all references which use hModule.
.text:00401282 sub_401282 proc near ; CODE XREF: sub_401337p
.text:00401282 push ebp
.text:00401283 mov ebp, esp
.text:00401285 add esp, 0FFFFFFF4h
.text:00401288 cmp hModule, 0
.text:0040128F jnz locret_401320
.text:00401295 push 200h ; nSize
.text:0040129A push offset ExistingFileName ; lpFilename
.text:0040129F push hInstance ; hModule
.text:004012A5 call GetModuleFileNameA
.text:004012AA sub eax, 0Ch
.text:004012AD mov ecx, offset ExistingFileName
.text:004012B2 add ecx, eax
.text:004012B4 mov byte ptr [ecx], 0
.text:004012B7 push offset ExistingFileName ; lpString2
.text:004012BC push offset LibFileName ; lpString1
.text:004012C1 call lstrcpyA
.text:004012C6 push offset aInfo_nfo ; "info.nfo"
.text:004012CB push offset ExistingFileName ; lpString1
.text:004012D0 call lstrcatA
.text:004012D5 push offset aAdvaapi32_dll ; "advaapi32.dll"
.text:004012DA push offset LibFileName ; lpString1
.text:004012DF call lstrcatA
.text:004012E4 push offset LibFileName ; lpFileName
.text:004012E9 call DeleteFileA
.text:004012EE push 0 ; bFailIfExists
.text:004012F0 push offset LibFileName ; lpNewFileName
.text:004012F5 push offset ExistingFileName ; lpExistingFileName
.text:004012FA call CopyFileA
.text:004012FF or eax, eax
.text:00401301 jnz short loc_401305
.text:00401303 jmp short loc_401322
.text:00401305 ; ---------------------------------------------------------------------------
.text:00401305
.text:00401305 loc_401305: ; CODE XREF: sub_401282+7Fj
.text:00401305 push 2 ; dwFileAttributes
.text:00401307 push offset LibFileName ; lpFileName
.text:0040130C call SetFileAttributesA
.text:00401311 push offset LibFileName ; lpLibFileName
.text:00401316 call LoadLibraryA
.text:0040131B mov hModule, eax
.text:00401320
.text:00401320 locret_401320: ; CODE XREF: sub_401282+Dj
.text:00401320 leave
.text:00401321 retn
.text:00401322 ; ---------------------------------------------------------------------------
.text:00401322
.text:00401322 loc_401322: ; CODE XREF: sub_401282+81j
.text:00401322 push 10h ; uType
.text:00401324 push offset aCrackme1ByVC_0 ; "CRACKME #1 BY V!CTOR - UNREGISTERED"
.text:00401329 push offset aT ; "??
.text:0040132E push 0 ; hWnd
.text:00401330 call MessageBoxA
.text:00401335 leave
.text:00401336 retn
.text:00401336 sub_401282 endp
No way.. attached nfo file was dll..
Anyway, try to analyze this code, you can see that it makes advaapi32.dll and copy data from info.nfo, and set hidden-file attribute.
(Here is a analysis code blow)
void __cdecl sub_401282()
{
if ( !hModule )
{
ExistingFileName[GetModuleFileNameA(hInstance, ExistingFileName, 0x200u) - 12] = 0;
lstrcpyA(LibFileName, ExistingFileName);
lstrcatA(ExistingFileName, "info.nfo");
lstrcatA(LibFileName, "advaapi32.dll");
DeleteFileA(LibFileName);
if ( CopyFileA(ExistingFileName, LibFileName, 0) )
{
SetFileAttributesA(LibFileName, 2u);
hModule = LoadLibraryA(LibFileName);
}
else
{
MessageBoxA(0, "??, "CRACKME #1 BY V!CTOR - UNREGISTERED", 0x10u);
}
}
}
So, it imports one strange function(CHK)..
Let's see the function in DLL file..

There's nothing interesting. It saves arguments to global variables.
But, when it works?
push offset ProcName ; "CHK"
push hModule ; hModule
call GetProcAddress
push offset byte_403200 ; Put Serial
push offset String ; Put Name
call eax ; return function's address from GetProcAddress
But, still we don't know where it checks serial..
When I search the whole codes there is only one place which call success message.
The fake check routine.
But we have dll file also.
When DLL file attach or detach from main module, program calls entry function, and this is only one remainder place that program can check the serial.
As might have been expected, I can find success message in a DLL string list.
Anyway, pick out serial check routine below...

If convert it to C code..
BOOL __stdcall DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
BOOL result; // eax@2
char *p2; // edi@5
char *p; // esi@5
if ( fdwReason == 1 )
{
result = 1;
}
else
{
if ( !fdwReason && buf1 )
{
p = buf1;
p2 = buf2;
while ( *p )
{
if ( (*p2 + *(p2+1)) != *p + 35 )
return MessageBoxA(0, "You are not regestered yet", "CRACKME #1 BY VCTOR", 0x10u);
++p;
p2 += 2;
}
result = MessageBoxA(
0,
"Congratulations, you are registered now!\r\n\r\n淃醴",
"CRACKME #1 BY VCTOR - REGISTERED",
0);
}
}
return result;
}
Anyway, we know how to check..
Make a keygen is your work.
(Or, just use my one)
Ohh.. One more..
Even if you enter a collect serial, it'll show you that your wrong.. (maybe it's a bug or point)
So, you can know that your serial is wrong or right, when you close a program..^^
keygen.exe
crackme1.7z


