티스토리 뷰
1편 : https://suspected.tistory.com/279
[개요]
본 글에서는 1편에서 소개했듯이 특정 커스텀 인코딩 루틴 분석 및 디코딩 스크립트를 제작하는 과정에 대해 설명한다.
[분석]
본 글에서는 1편에서 소개했듯이 특정 커스텀 인코딩 루틴 분석 및 디코딩 스크립트를 제작하는 과정에 대해 설명한다.
아래 코드는 0x18001B330 함수 호출 전 모습이다. 인자로는 v8 변수(인코딩된 데이터)와 v11 변수(데이터 길이 값)을 전달 받는다.

아래 코드는 디코딩 루틴(0x18001B330)이다. 아래 함수를 디버깅해서 문자열을 추출하는 방법도 있지만, 시간 소요가 크다는 단점이 있어 IDAPython을 이용해 스크립트를 작성한다. 코드가 많아보이지만 주요 기능은 별로 안된다.
__int64 __fastcall sub_18001B330(_QWORD *a1, __int64 a2) { __int64 v2; // r14 _QWORD *v3; // rbx unsigned __int64 v4; // r12 char *v5; // rsi __int64 v6; // rdi __int64 v7; // r15 bool v8; // cf _QWORD *v9; // rax unsigned __int64 v10; // rdi unsigned __int64 v11; // rax __int64 v12; // rsi __int64 v13; // rdx bool v14; // cf _QWORD *v15; // rax __int64 v16; // r8 __int64 v17; // r9 __int128 v18; // kr00_16 __int128 *v19; // rax __int64 v20; // rcx char v22[16]; // [rsp+20h] [rbp-50h] int v23; // [rsp+30h] [rbp-40h] __int128 v24; // [rsp+38h] [rbp-38h] __int128 v25; // [rsp+48h] [rbp-28h] __int64 v26; // [rsp+58h] [rbp-18h] v2 = a2; v3 = a1; v26 = a2; *(_QWORD *)&v25 = 0i64; *((_QWORD *)&v25 + 1) = 7i64; LOWORD(v24) = 0; sub_1800081A0((unsigned __int64 *)&v24, (__int64)&unk_180061E08, 0i64); v4 = v3[2]; if ( !(v4 & 1) && v4 > 0x20 ) { LODWORD(v26) = 0; WORD2(v26) = 0; v23 = 0; v5 = v22; v6 = 0i64; v7 = 16i64; while ( 1 ) { v8 = v3[3] < 8ui64; if ( v3[3] >= 8ui64 ) break; LOWORD(v26) = *(_WORD *)((char *)v3 + v6); v9 = v3; if ( !v8 ) goto LABEL_7; LABEL_8: *(_DWORD *)((char *)&v26 + 2) = *(unsigned __int16 *)((char *)v9 + v6 + 2); sub_18001BC10(&v26, L"%x", &v23); *v5 = v23; v6 += 4i64; ++v5; if ( !--v7 ) { v10 = 32i64; v11 = 0i64; while ( 1 ) { v12 = v11 - 16; if ( v11 < 0x10 ) v12 = v11; v13 = 2 * v10; v14 = v3[3] < 8ui64; if ( v3[3] >= 8ui64 ) break; LOWORD(v26) = *(_WORD *)((char *)v3 + v13); v15 = v3; if ( !v14 ) goto LABEL_15; LABEL_16: *(_DWORD *)((char *)&v26 + 2) = *(unsigned __int16 *)((char *)v15 + v13 + 2); sub_18001BC10(&v26, L"%x", &v23); v17 = (unsigned int)(char)(v23 ^ v7 ^ v22[v12]); v18 = v25; if ( (unsigned __int64)v25 >= *((_QWORD *)&v25 + 1) ) { sub_18001B8D0(&v24, *((_QWORD *)&v25 + 1), v16, v17); } else { *(_QWORD *)&v25 = v25 + 1; v19 = &v24; if ( *((_QWORD *)&v18 + 1) >= 8ui64 ) v19 = (__int128 *)v24; *((_WORD *)v19 + v18) = v17; *((_WORD *)v19 + v18 + 1) = 0; } LOBYTE(v7) = v23; v10 += 2i64; v11 = v12 + 1; if ( v10 >= v4 ) { *(_OWORD *)v2 = v24; *(_OWORD *)(v2 + 16) = v25; return v2; } } LOWORD(v26) = *(_WORD *)(v13 + *v3); LABEL_15: v15 = (_QWORD *)*v3; goto LABEL_16; } } LOWORD(v26) = *(_WORD *)(v6 + *v3); LABEL_7: v9 = (_QWORD *)*v3; goto LABEL_8; } *(_QWORD *)(v2 + 16) = 0i64; *(_QWORD *)(v2 + 24) = 7i64; *(_WORD *)v2 = 0; sub_1800081A0((unsigned __int64 *)v2, (__int64)&unk_180061E08, 0i64); if ( *((_QWORD *)&v25 + 1) >= 8ui64 ) { v20 = v24; if ( (unsigned __int64)(2i64 * *((_QWORD *)&v25 + 1) + 2) >= 0x1000 ) { v20 = *(_QWORD *)(v24 - 8); if ( (unsigned __int64)(v24 - v20 - 8) > 0x1F ) invalid_parameter_noinfo_noreturn(); } sub_1800295B0(v20); } return v2; }
아래부터는 위 함수 내 커스텀 알고리즘으로 인/디코딩된 루틴을 단계별로 설명한다.
첫 번째로, 1877b~~~27ca와 같은 인코딩된 문자열을 입력 받아 2개씩 끊어 Byte 형태로 변환을 우선 수행한다.
두 번째로, Byte 형태로 변환된 값 중 앞에서부터 16Byte, 나머지 Byte를 분리한다.

세 번째로, 위에서 나눈 것을 토대로 a ^ b ^ c 형태로 XOR을 하는 루틴이다.
우선, 16byte 왼쪽 부분부터 1씩 증가하는 a, 나머지 Byte 영역 부분부터 1씩 증가하는 c로 치환한다. b는 초기 값으로 0을 넣는다.
stage는 인코딩된 데이터(Byte 형태)의 갯수만큼 반복문을 수행한다. stage 1은 위에서 말한 바와 같이 왼쪽 16byte 영역의 첫 번째 요소인 0x18(a), 나머지 영역의 첫 번째 요소인 0x3d(c), b=0으로 치환 후 XOR을 수행한다.
stag 2부터는 a와 c는 한 칸씩 이동해 값을 불러오는 것이 동일하다. b같은 경우에는 이전(stage 1이 되겠죠?) c의 값을 b에 저장 후 XOR 연산을 수행한다. 아래 그림을 참고하면 이해가 쉬울 것이다.
stage를 계속 수행하다가 인코딩된 데이터(Byte 형태)의 갯수만큼 작동하고 끝이난다. 결과는 "%PDF-1.7..4 0 obj"

위 과정까지가 알고리즘 수행의 기본 루틴이고 아래는 알고리즘의 핵심 조건문 루틴이다. 3번에서 수행한 인코딩된 문자열 길이는 짧기 때문에 a영역이 16바이트가 되기 전에 끝났다.
하지만, 아래와 같이 긴 인코딩 문자열을 입력 받아 수행할 때는 a영역의 offset이 16바이트를 초과하면 어떤 요소에 접근해야 하는지에 대한 조건문이다. 아래 그림을 토대로 설명하면, stage 16까지 잘 수행하다가 stage 17을 수행하면 b와 c는 정상적으로 다음 요소를 순차적으로 저장한다. 그러나 a는 다음 요소가 c의 영역이기 때문에 a의 요소가 16이 넘어가는 순간 다시 0부터 다시 시작한다.
아래 stage 17과 같이 a가 0x89부터 다시 시작한다. 나머지는 순차적으로 값을 그대로 얻어와 XOR을 수행한다.
결과는 아래와 같다. 17번째 요소에 5가 들어가있는 것을 확인할 수 있다. "0602000000A4000052534131000400000100010005DA37C671C00B2A04759D5A143C015F4D0B38F0F83D6E4E19B309
D570ADB6EEA7CACB5A59A489B9E4B8D801B76A0C361E7D7798E6248722DC0349400857F68C5B21474138F0D3EE0929
AB1EBEA9EBB057E88D0CACB41D4A6029F459AD7B8A8D180B77DC4596745B9CF77DAD7B50F44B43DA8F1326E64C53
DAA51807A02751E2"

[구현]
위에서 분석한 알고리즘을 기반으로 이제 IDAPython을 이용해 디코딩 스크립트를 구현한다.
우리가 분석해야 하는 디코딩 함수 0x18001B330을 사용하는 함수를 확인하기 위해 Xref 기능을 사용해 확인한 결과, 중복도 있겠지만 239개의 함수가 있는 것을 확인했다.

우선 첫 번째로 0x18001B330을 call하는 부분부터 backward를 수행해 "lea rdx, a2c92cd0a32f60e" 부분으로 이동해 2번째 오퍼랜드의 값을 가져와야 한다. 이 루틴을 구현하면 239개의 인코딩된 데이터를 모두 추출해 리스트로 저장한다.

팁 : 데이터는 아래와 같이 유니코드 형태로 저장되어 있어 "GetString(str_address, strtype=idc.ASCSTR_UNICODE)" 형태로 작성해줘야 그대로 추출된다. 유니코드가 아닌 형태로는 1Byte씩 접근해 주소를 옮겨가며 추출해야 하는 것 보다 훨씬 편하다.

인코딩된 데이터를 리스트에 모두 저장한 후 이제 반복문을 돌려 디코딩 함수에 인코딩된 데이터, 데이터 길이 2가지를 인자로 넣어주면 끝이다.
아래 코드는 IDA(offset : 0x18001B330) 내 구현된 알고리즘을 기반으로 재구성했다. 알고리즘 형태는 위에서 설명한 것과 동일하므로 설명은 생략한다.
from idautils import * from idaapi import * from idc import * import re p = re.compile('(\d|[a-f]){20,}') def EncodedDataCollector(addr): encodeddataList = [] for addr in XrefsTo(0x18001B330, flags=0): count = 0 line = addr.frm while True: line = idc.PrevHead(line) count += 1 if GetMnem(line) == "lea" and GetOpType(line, 0) == o_reg and GetOpType(line, 1) == o_mem: str_address = GetOperandValue(line, 1) try: data = GetString(str_address, strtype=idc.ASCSTR_UNICODE) signal = 1 except: signal = 0 if signal != 1: break else: encodeddataList.append(data) if count > 10: break return encodeddataList def confirmRegex(lists): attemplist = [] expectinglen = len(lists) for r in lists: result = p.finditer(r) for r in result: zzz = r.group() attemplist.append(zzz) if expectinglen == len(attemplist): return 1 else: return 0 def str2binbyte(encdata): count = 0 bl = [] for i in range(0, len(encdata), 2): bl.append(int(encdata[i:i+2], 16)) if type(bl) == list: return bl # list else: return False def decoding_1B330(enc, enclen): v22=0 v7=0 v10_count=16-1 #count enc16byteleft = enc[:16] enc16byteRigth = enc[16:] decodedDataList = [] j = 0 for i in range(0, enclen): if j >= 16: j = 0 v23 = enc16byteleft[j] v22 = enc16byteRigth[i] dedata = v23^v7^v22 decodedDataList.append(dedata) v7 = v22 v10_count += 1 j+=1 if v10_count >= enclen-1: break finaldata = ''.join(map(chr, decodedDataList)) return finaldata def main(): decodingroutineaddr = 0x18001B330 result = EncodedDataCollector(decodingroutineaddr) #list type if len(result) > 0: if(confirmRegex(result)): for endata in set(result): encbytebin = str2binbyte(endata) if(encbytebin != False): enc_len = len(encbytebin) result = decoding_1B330(encbytebin, enc_len) print(result) main()
드롭퍼(DLL)를 IDA에 로드하고 위에서 구현한 디코딩 스크립트를 실행하면 아래와 같은 문자열들이 출력된다. API 함수, C2 주소, URL 파라미터, 바이너리 등 유의미한 정보들을 추출 할 수 있었다. xref로 확인했을 때는 239개였는데, 중복을 제거했더니 200개로 줄었다.
LookupPrivilegeValueW GetWindowTextW FindClose HttpAddRequestHeadersA ToAscii temp %d-%02d-%02d_%02d-%02d-%02d-%03d GetWindowTextA texts.letterpaper.press shlwapi.dll GetFileSize ConsentPromptBehaviorAdmin GetLocalTime SelectObject ReadFile KeyboardMonitor shell32.dll flags CreateRemoteThread LookupPrivilegeValueA SHCreateDirectoryExA S_Regsvr32 VirtualAllocEx RegQueryValueExA HttpSendRequestExA SetFilePointer GetWindowThreadProcessId 0702000000A400005253413200040000010001006D4582142BA47753E19FF39DBF232B7BAEE5141CC59AB328CA25EC21BEF955FE091F90B8FF3C3D8CD00973E3D2D7FACAD76B40A0A90BDE7468338B4F7C39DFDDE6C1574F3C48065AB364E505C322FF6B26CB67014DA28CD1FABEE32C9DB4BFD6F182AAA9DFB77EF3B26F91BC2E03EE4AB04B8A8741B83A85443DB8F28B99A3C63B206FAE6F36E19D4AFA768CF24283CFB7137FE47C403BC1E9E44CC12AB46877E7EAD66E69BC1C95E074127F1359978D8F6A8F5F57F15B220CACF213184176F9773E649A421A870340AFB025640A0EE5AFCA7DF1C7F6682FD59C9FEC241A9128D26608F5DDE2114CCA6815CA18B337C6B6FA713606A5ABE15133B061765AD8CB4181E72FC2DAA084FDB14AB05D38254556CBF2AF5DEB987F25206E657B03EAC21D2C7BD86703AEB22B27A456B60E3AD3C3C62D5DD7C06E5DB1C5D756B6F0019DFFFE3A425CDFDF9EA4E9F0085DD8064F97028A954A6D9DED2C06358EB77FF3CCC976B5E08344AC167B3971CBABBABD20E90C7BF4D1EA6F563B62B14C175D0683466190BFE8ECF4DDC69665233D21FD28825B7FFC4755A7A398E96FD94517D4B9F1EFFD99EAEB42F507F31D01594F0FF9FC03F4D5C3A40E4B64557C76CC4949BCFD96B77CD3F7A40DCB65B573E8C96A363EB2830B32ABD6827BACCF784BAC428F244861126235051C2B567FA9731699F1F6F76486CAC76DF407CBD0A52531AC87D6CC129B19D219E9CEE9E07AA3B7FAD0106F631B53B76D876F73CE642C3285ECB322473BCAD03B3647E716575FF530C57A078A2D5485BD13954E6FFA3C70C41F .enc InternetWriteFile SystemTimeToFileTime CreateMutexW kernel32.dll list.fdb GetCurrentProcess FindWindowA GetTempFileNameA Advapi32.dll WriteFile LoadLibraryA URLDownloadToFileW PromptOnSecureDesktop RegCreateKeyExA %PDF-1.7..4 0 obj User32.dll Desktop DeleteFileW InternetConnectA RegSetValueExW GetModuleHandleW Process32First GetVolumeInformationW CopyFileW OpenMutexW Gdi32.dll ExitProcess CreateMutexA FindNextFileA GetStartupInfoW URLDownloadToFileA :repeat del "%s" if exist "%s" goto repeat del "%%~f0" RegCreateKeyExW Software\Microsoft\Windows\CurrentVersion\RunOnce ESTCommon.dll MoveFileExW GetDIBits MessageBoxA HttpEndRequestW LoadLibraryW GetTempPathW GetTempPathA GetObjectA SHGetFolderPathW DeleteObject FindNextFileW InternetOpenW GetTokenInformation RegCloseKey CreateFileA SHCreateDirectoryExW CreateCompatibleBitmap Process32FirstW MoveFileExA GetTickCount CreateToolhelp32Snapshot Documents Process32NextW HttpOpenRequestW SpyRegsvr32-20210505162735 GetSystemTime FindFirstFileW CreateProcessW SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System AdjustTokenPrivileges OpenProcessToken ReleaseDC .zip urlmon.dll Downloads FolderMonitor OpenMutexA VirtualProtect .tmp GetDesktopWindow InternetSetOptionExA Wininet.dll HttpSendRequestA 0602000000A4000052534131000400000100010005DA37C671C00B2A04759D5A143C015F4D0B38F0F83D6E4E19B309D570ADB6EEA7CACB5A59A489B9E4B8D801B76A0C361E7D7798E6248722DC0349400857F68C5B21474138F0D3EE0929AB1EBEA9EBB057E88D0CACB41D4A6029F459AD7B8A8D180B77DC4596745B9CF77DAD7B50F44B43DA8F1326E64C53DAA51807A02751E2 GetVolumeInformationA GetModuleFileNameA GetWindowsDirectoryW regsvr32.exe ShowWindow HeapFree AppData\Local\Microsoft\Windows\INetCache\IE GetKeyState DeleteDC HttpQueryInfoW FreeLibrary HttpEndRequestA Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36 RegSetValueExA GetDC Process32Next InternetSetOptionExW CloseHandle InternetCloseHandle CreateCompatibleDC RegQueryValueExW InternetReadFile HttpSendRequestW powershell.exe start-process regsvr32.exe -argumentlist '/s %s' -verb runas PathFileExistsA WriteProcessMemory HeapAlloc GetForegroundWindow /s " GetSystemMetrics %s/?m=a&p1=%s&p2=%s-%s-v%s.%d DeleteUrlCacheEntryW CreateFileW GetParent GetProcessHeap WaitForSingleObject InternetOpenA ESTsoftAutoUpdate CopyFileA MessageBoxW cache Iphlpapi.dll FindWindowW RegOpenKeyExW GetModuleHandleA HttpOpenRequestA CreateProcessA HttpQueryInfoA VirtualAlloc GetVersion GetWindowsDirectoryA GetAdaptersInfo GetStartupInfoA GetKeyboardState Software\ESTsoft\Common 2.0 GetTempFileNameW OpenProcess list.ldb CreatePipe HttpAddRequestHeadersW BitBlt CreateThread RegDeleteValueW DeleteFileA TerminateProcess .bat ScreenMonitor SHGetFolderPathA SetProcessDPIAware RegDeleteValueA RegOpenKeyExA Win%d.%d.%dx64 Sleep SeDebugPrivilege GetNativeSystemInfo UsbMonitor GetModuleFileNameW DeleteUrlCacheEntryA GetDeviceCaps PathFileExistsW GetProcAddress FindFirstFileA --7263b57d61acd27d98a454fc484795fe0106d5 InternetConnectW VirtualFree HttpSendRequestExW GetWindow GetObjectW
'리버싱 > 윈도우 악성코드 분석' 카테고리의 다른 글
Windbg로 dll 분석 세팅하기 (0) | 2021.09.08 |
---|---|
jse dropper 악성코드 분석 (Kimsuky?) - 1편 (0) | 2021.09.08 |
악성코드 분석(암호화폐 거래소(플라이빗-Flybit) 타겟 스피어피싱 공격) (0) | 2020.04.29 |
악성코드 분석("[조회]비트코인 투자 카페 강퇴&활동정지" 악성 한글문서) (0) | 2020.04.29 |
악성코드 분석(2020 상반기 한국수력원자력 채용공고 사칭 악성 한글문서) (1) | 2020.04.28 |
- Total
- Today
- Yesterday
- Cisco Talos
- 출처 : Do it 안드로이드 프로그래밍
- idapython
- vuln
- Yara
- Static Analysis Engine
- Servey
- Bisonal
- 위협정보공유
- Flybits
- 스피어피싱
- CVE-2018-9375
- 멋쟁이사자처럼 4기
- .wll
- 해킹메일
- 악성코드
- MS-Office
- infostealer
- CVE-2018-0798
- VirusBulletin
- malware
- cuckoo-sandbox
- koodous
- AMSI
- 한글악성코드
- us-cert
- Decoding
- 비트코인
- Kimsuky
- keylogger
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |