반응형

Ghidra로 분석하면 아래와 같은 경고가 나오는 경우가 있다.

 

/* Warning: Call to offcut address within same funct: */

 

Replace.zip
0.01MB

 

 

디컴파일 윈도우에 경고가 표시된다.

함수 안의 다른 주소를 함수처럼 호출한다는 의미이다. 다른 프로그래밍 언어로 이런 개념의 코드를 작성하는 것은 이상하게 보이지만(함수 포인터 등을 이용하면 가능할 거 같기도 하다) 어셈블리 언어에서는 복잡하지 않게 작성할 수 있을거 같다. 이 디컴파일 내용의 어셈블리 명령을 살펴보자.

 

어셈블리 명령 윈도우

FUN_0040466f() 의 첫 번째 명령이 CALL LAB_00404674+6(0040467a) 이다. 잘 살펴보면 0040467a 라는 주소는 바로 아래 명령의 중간 부분이다. 뭔가 이상해서 첫 번째 그림에서와 같이 0040467a 호출 명령은 다른 함수들과 달리 소문자로 주석과 같은 분홍색으로 표시되는거 같다.

 

 

이 부분이 실제 어떻게 진행 되는지 x32dbg로 동적 분석을 진행해 보자.

지금은 00404674 주소에서 아래와 같이 명령이 구성되어 있다.

8105 D0844000 C7051660 add dword ptr ds:[4084D0],601605C7 

 

 

0040466F 에서 call replace.40467A 명령어가 실행되면 00404674 주소에 있는 명령의 중간부터 다시 해석되어 아래와 같이 명령이 바뀐다.

0040467A C705 16604000 EB609061 mov dword ptr ds:[406016],619060EB

 

 

이번엔 아래와 같은 경고를 살펴보자.

 

/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

 

밑줄(_)로 시작하는 전역 변수(여기서는 함수의 이름을 말하는거 같다)가 덮어 씌어진다는 경고인거 같다.

 

 

두 개의 노란색 코드를 보면 _FUN_0040466f()를 덮어 쓰고 있다. 이 경우 함수의 내용이 바뀌게 된다. 그래서 경고를 하는거 같다.

 

 

_FUN_0040466F 를 0xC39000C6 으로 덮어 쓰는 부분이다. 이 명령 실행 전과 후를 비교해 보자.

 

실행 전 0040466F 의 명령이다.

 

 

실행 후 명령이 바뀐것을 볼 수 있다.

 

반응형
Posted by J-sean
:
반응형

간단한 비주얼 베이직 프로그램으로 VB Decompiler 사용법을 익혀보자.

 

Music_Player.zip
9.52MB

 

 

Music_Player 실행 화면.

 

VB Decompiler로 Music_Player를 로드하면 간단한 프로그램 정보를 확인할 수 있다.

 

Forms - FrmMain에서 프로그램에 쓰인 여러가지 컨트롤 정보를 확인 할 수 있다.

예를 들어 STOP 버튼은 Caption으로 '■' 문자를 사용하고 (1680, 1560, 615, 495)에 위치하며 이벤트 처리 함수는 CMD_STOP이다.

 

CMD_STOP_Click() 의 디컴파일 내용을 확인 할 수 있다.

 

 

디스어셈블 내용도 확인 할 수 있다.

 

x32dbg에서 CMD_STOP()의 주소 00403EE0 로 이동해도 같은 내용을 확인할 수 있다.

 

ClsMCI에서 그 외 정의된 처리 함수들을 확인할 수 있다.

 

반응형
Posted by J-sean
:
반응형

프로그램에서 발생 시키는 예외를 처리하고 콜 스택을 확인해 보자.

 

Music_Player.zip
9.52MB

 

 

※ 참고

Music_Player.exe.dd32
0.02MB

x32dbg로 분석하며 정리한 comments 파일

[x32dbg설치 위치]\release\x32\db에 복사해 넣으면 comment로 파일 내 중요한 위치의 정보가 표시된다.

 

 

1분 듣기 제한이 있는 Music_Player에서 00404563에서 eax와 60,000을 비교한 후 eax가 60,000보다 작으면 0040456B에 의해 4045FE로 점프하게 된다. 그래서 이 부분을 무조건 점프하도록 jl에서 jmp로 바꾸면 60초 후, 아래와 같은 Run-time error가 발생한다.

 

 

이 상태에서 x32dbg로 돌아와 디버기를 Pause한다.

 

 

그러면 760D10CC에서 멈추게 되는데 이 부분은 중요하지 않다.

 

 

Call Stack 윈도우에서 우클릭 - Show Suspected Call Stack Frame을 클릭한다.

 

 

위에서부터 살펴보면 music_player의 004046BF 주소로 돌아가야 한다는 것을 알 수 있다. 004046BF로 가보자.

 

 

004046BF 바로 위 주소 004046B9에서 call dword ptr ds:[<__vbaHresultCheckObj>] 명령에 의해 Run-time error가 발생한것을 알 수 있다.

 

 

이번엔 Run-time error가 발생할 때 x32dbg가 자동으로 break를 걸도록 해 보자.

 

Options - Preferences - Exceptions - Unknown exceptions 를 선택하고 First chance와 Debugger를 선택하고 저장한다.

 

마찬가지로 0040456B에서 무조건 점프 하도록 명령문을 jmp 0x004045FE 로 바꾸고 음악 파일을 60초 이상 플레이한다.

 

 

Run-time error 윈도우가 뜨기 전, 위 그림과 같이 자동으로 브레이크가 걸린다. 이 부분은 중요하지 않으므로 콜 스택 윈도우로 가보자.

 

 

이번에도 우클릭 - Show Suspected Call Stack Frame을 클릭하면 위 그림과 같이 music_player로 돌아갈 주소 004046BF가 표시되어 있다.

 

 

004046B9 call dword ptr ds:[<__vbaHresultCheckObj>] 명령이 실행되지 않도록 아래 그림과 같이 004046AB의 jge를 jmp로 바꾼다.

 

 

2025.07.18 - [Reverse Engineering] - [Ghidra] iVar1 = (**(code **)(*piVar3 + 0x44))(piVar3,local_a8)의 의미

 

 

Music_Player에서 60,000과 비교하는 플레이 시간을 가져오는 함수를 조금 더 살펴보자.

 

 

00404563에서 eax와 60,000을 비교하기 전, 0040455D에서 [ebp-A4]의 값이 eax로 복사 되는걸 볼 수 있다.

 

 

그전에 00404539~00404545에서 [ebp-A4]의 주소값이 eax로 복사되고 스택에 push되어 함수가 호출된다. [ebp-A4]의 주소는 0019F9F0이고 호출되는 함수에서는 ebp+C 에서 이 값을 찾을 수 있다. 여기서 60,000과 비교할 값을 가져온다는걸 추측할 수 있다.

00404545에 브레이크 포인트를 걸고 어떤 함수로 이동하는지 살펴보자.

 

 

Step Into로 한 스텝 들어가 보면 여러 함수의 목록 테이블 같은 코드로 이동하게 된다. 그 중 00401A38은 4055A0로 이동하는 jmp명령이다.

 

4055A0는 HScrollBar의 위치를 가져오는 함수다.

 

플레이가 시작되면 음악 파일의 재생 위치 위치로 시간을 가져 오는게 아닌, 타이머를 1초 간격으로 호출하며 스크롤 바를 이동 시키고 다시 그 스크롤 바의 위치로 재생 시간을 구하는게 아닌가 싶다. (VB Decompiler 분석)

 

 

 

004055A0 함수의 내용을 보면 004055F5 에서 어떤 함수 호출 후 [ebp+C]의 값을 edx로 복사하고 004055FE 에서 그 함수의 리턴값을 edx가 가리키는 위치로 복사하는 것을 볼 수 있다. 여기서 [ebp+C]는 이전 함수에서 60,000과 비교할 값을 저장할 [ebp-A4]의 위치 값(0019F9F0)이다. 바로 플레이 시간이 저장되는 것이다.

 

ebp+C 에 들어있는 값 = 0019F9F0

 

 

그런데 004055FE 명령 전 까지 진행 된 상태에서 레지스터를 보면 EBX, EDI 값이 0인걸 알 수 있다.

 

 

그래서 004055FE mov dword ptr ds:[edx],eaxmov dword ptr ds:[edx],edi로 바꾸면 플레이 시간이 항상 0으로 인식될 것이다. 하지만 이렇게 하면 HScrollBar도 항상 0인 위치를 가리키게 된다.

 

반응형
Posted by J-sean
:
반응형

Ghidra에서 (**(code **)(*XX + 0xYY))(ZZ) 형태의 명령어로 디컴파일 되는 경우가 있다.

이때, (**(code **)(*XX + 0xYY))는 함수를 가리키는 포인터, (ZZ)는 함수의 파라미터 부분인거 같다.

 

C++의 가상함수 테이블에서 이런 경우가 생기는거 같고, Visual Basic에서도 생기는거 같다.

함수의 위치가 정확히 정해진게 아니고 레지스터 값을 이용해 위치를 계산해야 하기 때문에 이렇게 되는거 같다.

 

※ 참고

Stack Overflow

Functions of the Visual Basic 6.0 Library

 

Music_Player.zip
9.52MB

 

 

Ghidra 분석

 

piVar3, local_a8은 모두 포인터이며 특히 여기선 (**(code **)(*piVar3 + 0x44)) 호출 후 local_a8에 값이 저장된다.

 

x64dbg 분석

Ghidra에서 분석한 포인터 local_a8은 [ebp-A4]이다.

0040455D에서 [ebp-A4]의 값을 eax로 옮기고 00404563에서 cmp eax, EA60 명령으로 60,000과 비교하고 있다.

(0xEA60 = 60,000)

 

 

 

정적 분석(Ghidra)으로는 (**(code **)(*piVar3 + 0x44))라는 함수의 위치를 파악하기 어려운거 같다. 동적 분석(x64dbg)으로 00404545 call dword ptr ds:[edx+44] 명령이 이동하는 곳을 따라가 보면 0x004055A0인것을 알 수 있고 함수로 인식되어 있다.

 

Ghidra에서도 2개의 포인터를 인수로 받는 FUN_004055a0라는 함수로 인식되어 있다.

 

  local_1c = 0;
  local_20 = 0;
  (**(code **)(*param_1 + 100))(param_1,2,&local_20);
  local_1c = local_20;
  (**(code **)(*param_1 + 8))(param_1);
  *param_2 = local_1c;

 

디스어셈블된 FUN_004055a0의 코드 중 하단부를 다시 살펴 보면 1~2 번째 줄에 의해 local_1c, local_20은 0으로 초기화 되고 3 번째 줄 함수 호출에서 파라미터로 넘어간 local_20에 어떤 값이 저장되어 4번째 줄에서 local_1c에 다시 저장된다. 그리고 마지막 줄 *param_2 = local_1c; 명령에 의해 제일 위 그림에서 설명했던 local_a8에 값이 저장된다.

(이전 함수에서 파라미터로 넘긴 local_a8이 지금 함수에서 param_2다)

 

2025.07.19 - [Reverse Engineering] - [x32dbg/x64dbg] Exception Handling and Call Stack Check 예외 처리 및 콜스택 확인

 

반응형
Posted by J-sean
:
반응형

기드라에서 인식하지 못하는 유니코드 문자열을 인식시켜 보자.

 

Music_Player.zip
9.52MB

 

x64dbg로 분석 시 제대로 인식되는 유니코드 문자열이 있다.

 

이 부분을 Ghidra로 분석해 보면 문자열이 인식되지 않는다.

 

문자열 코드로 이동한다. 함수로 인식되어 있다.

 

문자열 부분을 모두 선택하고 우클릭 - Clear - Clear Code Bytes를 클릭한다.

 

 

함수로 인식됐던 내용이 삭제되면 다시 우클릭 - Data - TerminatedUnicode 클릭

 

유니코드 문자열로 제대로 인식됐다.

 

디스어셈블리에서도 제대로 인식된다.

 

사실 x64Dbg에서도 문자열 코드의 디스어셈블리 내용을 보면 문자열로 인식되지는 않는다.

 

※ 참고

0x00402bac의 문자열 부분 Hex Code 는 아래와 같다.

310084BD2000F8BBACB9E3B430AECCB9200000ACA5B269D5C8B2E4B22E00

 

유니코드는 한 문자가 2바이트로 구성되며 Little Endian 방식이므로 뒤집어서 해석해야 한다.

예를 들어 '분'이라는 글자는 유니코드로 bd84이지만 Hex Code에는 84bd로 저장되어 있다.

 

그러므로 '1분 미리듣기만 가능합니다.'의 유니코드는 아래와 같다.

0x0031 0xbd84 0x0020 0xbbf8 0xb9ac 0xb4e3 0xae30 0xb9cc 0x0020 0xac00 0xb2a5 0xd569 0xb2c8 0xb2e4 0x002e

 

이 링크에서 위 유니코드를 Input에 입력하면 변환된 문자열을 확인 할 수 있다.

 

 

 

반응형
Posted by J-sean
:

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

반응형

x64dbg에서 Conditional Breakpoint나 Trace into/over 사용 시 작성하게 되는 Break Condition, Log Condition 에 대해 알아보자.

 

Conditional Breakpoint

Break Condition을 평가해서 True(1이나 0이 아닌 값)인 경우 break 한다. 기본값은 1(True)이다.

Log Condition을 평가해서 True(1이나 0이 아닌 값)인 경우 Log Text를 기록한다. 기본값은 1(True)이다.

Break 시 마다 Hit Count가 증가한다.

 

예)
절대 break하지 않는 Conditional Break. (디버거를 멈추지 않고 명령만 실행하거나 로그를 남기고 싶을때 유용)
break condition: 0

EAX와 ECX가 모두 1일 경우 break.
break condition: EAX==1 && ECX==1

첫 번째 매개변수가 1일 경우 break.
break condition: arg.get(0)==1

EAX가 유효한 주소값을 가질 경우 break.
break condition: mem.valid(EAX)

Break Point를 세 번째 만나면 break.
break condition: $breakpointcounter==3 or ($breakpointcounter%3)==0

ECX 값 위치의 utf-16 문자열이 "foo"를 포함하면 break.
break condition: strstr(utf16(ECX), "foo")

 

Trace over

Break Condition을 평가해서 True(1이나 0이 아닌 값)인 경우 break 한다. 기본값은 0(False)이다.
Log Condition을 평가해서 True(1이나 0이 아닌 값)인 경우 Log Text를 기록한다. 기본값은 1(True)이다.

Maximum trace count에 trace 할 최대 값을 지정할 수 있다.
Record trace를 선택하면 trace 내용이 파일에 기록되고 Trace 탭에서 확인 할 수 있다.

 

모든 숫자는 기본적으로 16진수로 인식된다. 10진수를 사용하고 싶다면 .123 처럼 숫자 앞에 . (dot) 을 사용한다.

예) .10 == a, .16 == 10, .18 == 12

 

Trace into/over는 step into/over를 Maximum trace count에 지정된 숫자만큼 자동으로 진행하는 것이다. 예를 들어 Log Text에 eax: {eax}를 입력하고 Maximum trace count에 2를 입력한 후 OK를 클릭하면 Log 창에 아래와 같은 기록이 남는다.

eax: 4E292C
eax: 22B1010
Trace finished after 2 steps!

 

Expressions

+, -, *, /, %, ~, >, <, =, ==, !=, ^, &, >>, <<, &&, || 등 대부분의 C 언어와 같은 문법으로 표현 가능하다. Help - Calculator 나 하단의 Command 창에서 연습해 볼 수 있다.

숫자, 변수, 레지스터(eax, ebx 등), 플래그(_zf = Zero Flag), 메모리 값(n:[00401238] = 00401238위치에서 n바이트를 읽어온다: n은 32비트에서 4, 64비트에서 8을 넘을 수 없다), 레이블/심볼 등이 표현에 사용 될 수 있다.

String Format

x64dbg에서 사용하는 기본적인 문자열 포맷은 {?:expression}이다. ?는 옵션이며 아래와 같은 type이 들어갈 수 있다.

기본 타입
d: signed decimal: -3
u: unsigned decimal: 57329171
p: zero prefixed pointer: 0000000410007683
s: string pointer (not recommended, use {utf8@address} instead)
x: hex: 3C28A (default for integer values)
a: address info: 00401010 <module.EntryPoint>
i: instruction text: jmp 0x77ac3c87

복합 타입
{ascii[;length]@address} will print the ASCII string at address with an optional length (in bytes).
{ansi[;length]@address} will print the ANSI (local codepage) string at address with an optional length (in bytes).
{utf8[;length]@address} will print the UTF-8 string at address with an optional length (in bytes).
{utf16[;length]@address} will print the UTF-16 string at address with an optional length (in words).
{disasm@address} will print the disassembly at address (equivalent to {i:address}).

예)
eax: {eax} => eax: 4C76
string: {utf16@4092a0} => string: Hello World!
password: {utf16@4*ecx+0x402000} => password: s3cret
return address: {a:[esp]} => return address: 6828371A

 

반응형
Posted by J-sean
: