악성코드 하나를 분석하던 중, 아주 흔하면서도 지금까지 짜증나게 만들었던 루틴이 하나 등장했습니다.

단순하게 LoadLibrary와 GetProcAddress를 통해 모듈 핸들과 함수 주소를 가져오고, 이를 전역변수에 대입하여 다른 코드들에서 사용하는 것으로 IAT에 자신이 사용하는 함수명을 표시하지 않게 하는 흔한 기법이지만, 몇가지 문제점이 있었습니다.



[그림 1. 첫번째 문제]


 첫째, 양이 많았습니다. 한 함수 내에서 40개가 넘어가는 함수들의 주소를 가져오는데, 이는 다음 문제점과 더해 미친듯한 시너지를 발휘합니다.



[그림 2. 두번째 문제]


 둘째, 변수명이 스택에 저장되어있습니다. 바이트 단위로 할당되어있기 때문에 보통이라면 이걸 전부 아스키 문자로 바꿔서 하나하나 읽고 무슨 함수인지를 파악해야 합니다. 함수가 엄청나게 많다 보니 1500바이트가 넘어가는 양입니다.




[그림 3. 세번째 문제]


 셋째, 변수 값이 암호화되어있습니다. 정확히는 0x08이라는 고정된 값으로 XOR되어있는 것이지만, 어쨌든 그냥 봐서는 어떤 함수인지 알기가 힘듭니다. 셋중 하나라도 문제가 없었으면 그냥 복사해서 파이썬 스크립트로 복호화하거나 해서 하나하나 네이밍해줬겠지만, 도저히 그럴 양이 아니었습니다.



 여기에서 잠시 현자타임이 왔고, 이걸 어떻게 해결할까 하다가 지금까지 너무 무식하게 분석해왔다는 생각이 들어서 IDAPython을 이용하여 자동 스크립트를 짜기로 결심했습니다.




[그림 04. 스크립트 동작 전]


 작성한 스크립트를 돌리기 전의 모습입니다. 



[그림 05. 스크립트 로그]


 스크립트를 동작시키면 IDAPython 콘솔창에 로그가 남습니다.




[그림 06. 스크립트 동작 후]


 스크립트를 동작한 후 새로고침해주면 위와 같이 GetProcAddress를 이용하여 대입한 값들이 네이밍됩니다. 예전 BoB 4기에서 잠깐 한 적이 있기는 했는데 사실 기억에는 전혀 남아있지 않아서 혼자 여기저기 검색하면서 조잡하게 짰습니다.


이 스크립트에는 아직 개선할 점이 몇 가지 있습니다.


- GetProcAddress의 결과를 전역변수가 아닌 지역변수나 Struct의 멤버변수같은것에 대입하는 경우는 네이밍이 되지 않습니다.

- 특정 함수 내에 커서를 둔 채로 실행해야 하며, 해당 함수 내에서의 GetProcAddress에 대해서만 네이밍을 진행합니다.

- GetProcAddress함수의 주소를 직접 구해서 RenameGetProcAddrVars 함수에 인자로 넣어야 합니다.


스크립트를 사용하면서 나중에 조금씩 업데이트해갈텐데, 그러면서 하나씩 해결해갈 예정입니다.


소스코드 및 정리한 내용은 아래에 있습니다.


IDAPythonUtils.py




 소스코드 내에서 사용한 idaapi의 함수들은 다음과 같습니다.


※ ea라는 용어는 Effective Address의 약자로, 주소값을 의미합니다.

- Byte : 인자로 ea를 받으며 해당 주소의 1바이트 값을 반환합니다. 리터럴 상수처럼 정적으로 값이 확인 가능해야 합니다.
- GetFunctionAttr : 첫번째 인자로 ea, 두번째 인자로 받아올 함수의 속성을 입력받아 해당 함수의 속성을 가져옵니다. 여기에서는 함수 시작 주소와 끝 주소를 받아오는데에 사용했습니다.
- GetDisasm : 인자로 ea를 받아 해당 주소의 값을 디스어셈블하여 문자열로 리턴합니다.
- GetOpnd : 첫번째 인자로 ea를, 두번째 인자로 가져올 오퍼랜드를 0-Base로 입력받습니다. 디스어셈블된 오퍼랜드 자체를 넘겨줍니다. (예:  lea eax, [esp+674h+var_617]의 경우 :[esp+674h+var_617]: 리턴)
- GetOperandValue : 첫번째 인자로 ea를, 두번째 인자로 가져올 오퍼랜드를 0-Base로 입력받습니다. 오퍼랜드에 넘어간 값을 강제로 int형으로 바꿔서 반환하는 것 같습니다. (예: lea eax, [esp+674h+var_617]의 경우 93 리턴)
- here : 현재 커서가 가리키는 주소 반환
- PrevHead : ea를 인자로 받아 바로 이전 명령의 주소 반환
- NextHead : ea를 인자로 받아 바로 이후 명령의 주소 반환
- XrefsTo : ea를 인자로 받아 해당 주소를 참조하는 주소들을 iterable하게 반환




블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,


 사실 무척 간단한 테크닉인데, 지금까지 모르고 계속 모든 struct를 직접 만들었다가 이제야 알게 되어 작성해 본다.


[직접 만드느라고 고생한 struct PEB_LDR_DATA]



 위 이미지는 직접 한땀한땀 만들어준 struct이다. 악성코드를 분석하는 도중 PEB에 접근하여 LDR을 가져오는 루틴이 있었고, 이를 좀더 보기 쉽게 하기 위해서 struct를 만든 것이다. LIST_ENTRY라는 struct도 따로 필요해서 직접 만들어 주었고, 여러모로 귀찮은 작업이 많았다. 그런데 알고 보니 PEB_LDR_DATA와 같은 윈도우에서 사용하는 범용적인 struct들은 이미 IDA에서 전부 만들어뒀다고 한다.



[Type libraries]


 View->Open subviews->Type libraries 혹은 단축키로 Shift+F11을 눌러보자. 'Loaded Type Libraries'라는 탭이 하나 추가되는 것을 볼 수 있다.



[Load New Type Library]


 우클릭->Load type library 또는 단축키 Ins를 누르면 위와 같은 창이 뜨면서 원하는 타입 라이브러리를 추가할 수 있다. 상당히 많은 라이브러리들이 있는데, 여기서 자기가 원하는 타입이나 struct가 선언되어 있는 라이브러리를 선택해서 로드하기만 하면 직접 struct 구조를 생성해 줄 필요 없이 그대로 사용 가능하다. 이제 위의 ntapi를 로드한 후, struct 탭에서 해당 struct를 생성하거나 변수에 타입을 지정해보자.



[자동으로 로드되는 타입들]


 놀랍게도 위와 같이 자동으로 struct가 생성되고 반영된다. 

나는 지금까지 무슨 삽질을 하고 있었던 것인가... 역시 머리가 나쁘면 몸이 고생한다.






블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,