본문 바로가기

쓰기

일단 FrigateBird(잘 알려진 피시로 호칭)님의 ATCode 20111003 (줄여서 1003버전) 을 한번 훑어보았는데요,

 

1003 버전의 주된 변경점은 Siglus 엔진에 대한 지원이 주를 이루고 있습니다.

물론 기존 ATCode 의 버그를 잡은 것도 있지만 마이너하니 넘어갑니다.

 

제가 보기에는 이 기능은 OVERWRITE 나 PTRCHEAT 에 부가적으로 붙이는 기능이 아니라 아예 새로운

후킹 방식으로 넣어야 할 듯 합니다. 완전히 내용이 다르거든요.

 

OVERWRITE, PTRCHEAT, SOW 등은 문자열을 직접 가리키는 포인터를 가져오는 형태인 데 반해,

이 방식은 Siglus 의 문자열 정보를 가지는 것 (아마도 클래스) 을 가리킵니다. 그리고 그 정보를

찾아 실제 문자열을 읽고 난 후 다시 정보를 돌려넣는 방식입니다. 뭐랄까 OVERWRITE와 PTRCHEAT의

중간 방식이랄까요;

 

그래서.. 제 생각에는 이 방식을 OVERWRITE, PTRCHEAT, SOW 에 이은 4번째의 후킹 방식으로

빼거나, 혹은 SCP 처럼 새로운 플러그인으로 분리하는 것이 좋을 것 같습니다.

 

제가 이해한 Siglus 문자열 클래스의 형식은 다음과 같습니다.

 

struct _SIGLUS_STRING {

        union {

                LPSTR pszString;        // MBCS, 글자수 16자 (16바이트) 이상

                LPWSTR pwszString;        // UNICODE, 글자수 8자 (16바이트) 이상

                BYTE szString[16];        // MBCS, 글자수 15자+/0 까지

                WCHAR wszString[8];        // UNICODE, 글자수 7자+/0 까지

        } str;

        DWORD dwLen;        // 글자수

        DWORD i;                // 이건 뭔지 잘 모르겠습니다. 계산식은 i=(dwLen/0x10+1)*0x10-1

};

 

그래서..

1) 글자수 15자 미만 (MBCS 기준)

DWORD   _______0_______1_______2_______3_______4_______5

WORD    ___0___1___2___3___4___5___6___7___8___9___A___B

BYTE    _0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_0_1_2_3_4_5_6_7

0x1000: _a_a_r_i_g_a_t_o_u_u_!_!00      0C00    0F00   

dwLen=0x0C(12), i=(0xC/0x10+1)*0x10-1=(0+1)*0x10-1=0x10-1=0xF

-> 앞의 16바이트 부분에 글자를 직접 넣음

 

2) 글자수 15자 이상 (MBCS 기준)

DWORD   _______0_______1_______2_______3_______4_______5

WORD    ___0___1___2___3___4___5___6___7___8___9___A___B

BYTE    _0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_0_1_2_3_4_5_6_7

0x1000: 34120000                        1600    1F00   

0x1234: _a_a_r_i_g_a_t_o_u_u_g_o_z_a_i_i_m_a_s_u_!_!00

dwLen=0x16(22), i=(0x16/0x10+1)*0x10-1=(1+1)*0x10-1=0x20-1=0x1F

-> 첫번째 DWORD가 포인터로 실제 문자열의 위치를 가리킴

 

문자열을 직접 일본어를 넣으니 줄이 비틀려서 영어로 넣었습니다; _a_a = あ, _r_i = り... 등등.

 

대충 이런 식으로 작동하는 듯.

 

 

 

그나저나 피시 님이 제게 넘겨주신 코드는 중간단계 (완성품이 아닌) 인지 이상한 계산식에

버그같은 부분도 많이 보이더군요.

 

예를 들면

    if ((pCmd->GetBufLen() &&  pCmd->GetTransMethod() == 2)

            ||(pCmd->GetSmbuf() &&  pCmd->GetTransMethod() == 2 )){
     if(*(&(*(DWORD*)pArgText)+4) >= 0x10){

         szText = (LPSTR)*(&(*(DWORD*)pArgText)); 

     }
     else {

      if(*(&(*(DWORD*)pArgText)+4) > 0xFF) continue; // 1번

 

      if(*(&(*(BYTE*)pArgText)+*(&(*(DWORD*)pArgText)+4))!=0x00){ // 2번
       szText = (LPSTR)*(&(*(DWORD*)pArgText));
      }
      else szText = (LPSTR)pArgText;
     }
    }

 

이 코드에서

1번

- 여기서는 *(&(*(DWORD*)pArgText)+4) < 0x10 밖에 안 넘어올텐데 0xFF 보다 큰 값이 안나오므로

  이 if 문은 무조건 거짓이 뜰겁니다..

2번

- *(&(*(BYTE*)pArgText)+*(&(*(DWORD*)pArgText)+4)) 라는 식이 무엇을 의미하는지 모르겠군요;

  (*(&(*(BYTE*)pArgText) != 0x00) && (*(&(*(DWORD*)pArgText)+4)) != 0x00)

  을 의미하는 것일까요?

 

이외에도 그냥 ECX 같은 것만 인자로 들어왔을때의 문제라던가.. PTRCHEAT 시의 문제도 보이는 것 같고요.

제가 잘못 이해했을 수도 있겠습니다만..; 실제 업데이트시에는 전체적으로 고쳐야 할 지도 모르겠습니다.

분류 :
Talk
조회 수 :
4958
등록일 :
2011.11.02
19:21:50
엮인글 :
https://arallab.hided.net/48293/43d/trackback
게시글 주소 :
https://arallab.hided.net/board_devtalk/48293

FrigateBird

2011.11.12
14:20:06

몇가지 던지고 가자면

Siglus 엔진 만이 아닌, QLIE엔진이나, GIGA사에 사용중인 엔진 몇개와, 아야카시 를 낸 회사의 현재 나오는 게임들 및 그와 같은 방법으로 REP을 사용하거나 특정 함수에 거칠때는 그러한 형식이 되는게 간간히 보이는 경우가 있더군요.

 

그래서 예전부터 건의했던 특정 길이 지점 지정하여 번역 길이 덮어쓰기 를 대충 저 형식에 맞는것만 바꿔놓도록 했습니다.

 

그리고 코드상 1번 같은 경우는 제가 귀찮아서 안지웠습니다. ㅡㅡa

원래는 디버거로 스크립들이 어떻게 넘어오는지 감시용으로 썻다가...

우홋. 하고 보고나서 괜찮네 하면서

해당 메모리 감시 끝나고 나서 포인터 체크로 위에

제대로 갈겨놓고는 1번 보고 "어? 별 상관 없겠지 ㅋ.ㅋ 지우기도 귀찮긔"

하면서 넘어갔다는 후문이...(도주)

 

2번 같은경우는

(*(&(*(BYTE*)pArgText) != 0x00) && (*(&(*(DWORD*)pArgText)+4)) != 0x00)  는 절대 아니라고 답변 드릴 수 있습니다.

^-^

 

if(*(&(*(DWORD*)pArgText)+4) >= 0x10) 로 인해서

메모리 상에 한 라인보다 클 경우 포인터로 넘어가는데

후킹 장소가 포인터라고 될경우 1,2번이 있는 else  문으로 갑니다.

그러면 후킹 장소 + 0x10 에 있는 값은 길이가 되는데

 

*(&(*(BYTE*)pArgText)+             //후킹장소 및 포인터라고 판정된곳

*(&(*(DWORD*)pArgText)+4))   //후킹장소+0x10 에 길이값이 있을때

두 값을 더한 주소에 들어 있는 내용 값은 널문자인 0x00이 들어있을 경우

길이와 포인터는 문제가 없는 대사이다.

라고 체크 포인트라고 생각하시면 됩니다.

 

실제로 명령 포인터 값만 가고

밑에 0x10 장소에 길이를 더해도 그 곳에는 대사가 없는 경우가 있더군요. 

FrigateBird

2011.11.12
14:22:20

+ 덧으로 PTRCHEAT  의 집어처넣은건 그냥 없애도 될것 같네요.

 

메모리 덮어쓰기에서 값이 작을경우 자동으로 PTRCHEAT 형태로 비슷하게 넘기는 방법으로 했으니...

결국엔 저건 에러만 나오고 필요도 없었습니다.

 

(실제로 코드게에 올린것중 PTRCHEAT 에서  저 기능을 사용한 건 없습니다. 가다가 버퍼 에러도 나구요.)

 

ATCode에 넣은 이유는 2가지 입니다.

1. 귀찮아서

2. 실제로 저런 형태로 된 게임이 1만개중 20~30%정도는 되더군요.

 

그리고 코딩한 것들이 괴상 망측한 이유는

^-^ 실제 수정하여 디버거로 비교해보면 자연히 알게 됩니다.

컴파일러 강간하기

whoami

2011.11.12
14:41:08

아. 그러면 1번 부분은 그냥 불필요한 코드가 맞군요.

그리고 2번 부분은 문장 끝 부분의 \0 이 실제 있는지 없는지 확인하는 코드인건가요?

 

또.. PTRCHEAT 부분의 코드.. 제대로 작동하던가요? 현재 공개된 코드는 찾아보니 전부 OVERWRITE

를 쓰는 것 같고.. 특별히 PTRCHEAT 를 쓰지 않아도 무방해 보이는 것 같던데요.

 

아참, BUFCHANGE 와 LENCHANGE 의 정확한 차이점도 알고 싶은데요..

원본이 0x10 이하였더라도 번역이 0x10 이상이라면 알아서 포인터로 빼고, 그 반대라면

알아서 글자를 채워넣어버리 (고 원본의 문자열 버퍼를 해제시키)면 될텐데 특별히 저 옵션

두개로 나눠놓으신 이유가 궁금하네요. 저렇게 하지 않으면 에러가 발생하나요?

 

마지막으로.. 이 기능을 기존 OVERWRITE/PTRCHEAT/SOW 말고 새로운 방식으로 하거나 다른 알고리즘

플러그인으로 뺀다면 이름을 뭘로 짓고 싶으신지.. ㅎㅎ .. 궁금하네요.

FrigateBird

2011.11.13
17:38:29

2번은 \0 인지 확인하는게 맞습니다.

실제로 아닐 경우가 있었기 때문에;

 

PTRCHEAT 쪽은 위쪽에서 말한대로 없어도 됩니다.

실험중 나온 부산물에 불과 합니다.

 

BUFCHANGE 와 LENCHANGE 의 정확한 차이점을 얘기하자면 좀 길어지는군요;

원래는 PTRCHEAT 부분에서 시도를 먼저 했습니다.

 

그러면서 BUFCHANGE 를 넣고는 시도하고 값이 작은 글은 그냥 OVERWRITE 로 대충 끼워맞추자

하면서 엄~청 하기 싫어하면서 되게만 해보자 하고 덤볐다가

하고보니 되긴 됬지만 이상하게 아무데서나 2~3글   PTRCHEAT 로 넘기니 랜덤적으로 튕기는 현상이 발생하더군요.

(이유는 아직 못찾았습니다. 디버거로 봐도 제가 건드린 부분에서는 튕기는게 아니에요)

 

사실, 욕심도 생겼습니다.

기왕 여기까지 와버린거  제대로 OVERWRITE에 들이대자 하고 PTRCHEAT는 손을 놔버리고는

 

OVERWRITE에 BUFCHANGE 처넣고 하다보니..

작은 글을 번역에서 커질 경우를 깜빡하고 말았습니다. 참 어처구니 없는 결말

(진짜 이때 미쳤나 봅니다.)

한창 디버거로 구경하면서 애지중지 봤는데;;늅늅

 

그래서 다시 보기도 귀찮으니까 살짝 추가만 하자

싶은 생각으로 LENCHANGE 대충 만들고 약간 껴맞춰서 실행하니

결과는 나왔지요..

 

헌데...불미스러운 사태로 이상하게 또 튕기는 녀석

 

열받아서 BUFCHANGE 에다 싸그리 담았네요.

 

결과...  LENCHANGE는 BUFCHANGE를 사용할때 자동 사용으로 바뀌고.

PTRCHEAT 에서 사용할려고 놔둔 LENCHANGE 는 쓰레기로 변해서

 

결론적으로는 OVERWRITE 에 BUFCHANG 말고는 다 폐기 처분 대상  

 

 

 

결론, 귀찮니즘에서 시작된 참혹한 결과

 

지금 생각해보면 그거 정리하기 싫어서 여기다 올린거 같............(퍽퍽)

 

그리고 명칭따위는 아무래도 좋으니 얼른 기능이 필요했네요.

약 3년전인가.  히데한테 대사 밑에 길이 처 들어간게 있는데 좀 해줘요.

하니까. ㅇㅇ 나중에 라는 말이 돌아왔다지..

 

그런데 요즘들어 저런 식으로 된 게임만 대량 만나다 보니...

아 저런 형태로 가지고 노는 특정 함수가 있나보다 싶었지요.

FrigateBird

2011.11.13
18:24:30

추가 적으로 유니코드에서는 테스트를 했지만

아스키에서는 제대로 못했네요.

 

아마 버그가 있을지도 몰라요?

 

.............

FrigateBird

2011.11.13
19:53:59

그 뭐냐 추가적으로 손봐야되는게 있습니다.

 

그 형식을 따르되 0x10 이상이 될때 포인터로 갈아버리지 말고 해당 메모리 안을 덮어버리는 것과

길이 제한을 활성화 하여 그 해당 메모리 안에 원문 만큼 혹은 버퍼 무시도 지원 되면 좋겠네요.

 

그건 깜빡했습니다.

Siglus 엔진에서는 필요 없었어도, NeXAS(이하 GIGA사에서 사용중인) 엔진에서는 중복 포인터를 사용하기 때문에 저 기능이 있어야 코드화 시킵니다.

 

다른, 아야카시 게임에 적용한 엔진에는 대충 사용하긴 합니다.

FrigateBird

2011.11.13
20:01:07

"비밀글입니다."

:

whoami

2011.11.13
22:09:48

ㅎㅎ... 본체 작업 하는 것 때문에 이번에 할당된 자원(응?) 을 다 써버려서 아직 ATCode 쪽은

손을 못 댔네요. 본체도 일단 일단락시켜 놨으니 (숏컷 제작 부분에 약간의 버그가 있긴 합니다만

큰 문제는 아니니 일단 패스) 다음 시간나면 작업을 시작할 예정입니다.


추가적으로 손봐야 한다는 곳에 대해 좀 더 자세히 설명해 주시면 감사하겠습니다.

위의 형식을 따르되 0x10 이상이 될 때 포인터로 넘기지 말고 일반 OVERWRITE 처럼 뒷자리를

짤라서 15자 제한을 주자는 뜻이신가요? 버퍼 무시라면 포인터를 따로 주지 않고 15자 이상

집어넣는다? 그러면 dwLen 이나 i 부분을 침범해서 에러가 날 것 같은데요?


그리고.. 중복 포인터라는 것의 개념도 잘 안잡히는데 설명좀..;;

FrigateBird

2011.12.10
16:16:00

"비밀글입니다."

:
List of Articles
공지 Talk [필독] 테스트필터 사용시 주의사항
라파에
155445   2008-08-03 2008-12-16 00:03