728x90 AdSpace

.
Latest News

Shellcode Tutorial 5: Function Hash Generation


Introduction
In the previous tutorials we have always had hardcoded addresses pointing to Windows functions that we wanted to call. This prevents the shellcode from being executable on multiple Windows operating systems, service packs, or even differing patch levels.
This tutorial is the first step in moving towards a more dynamic Windows shellcode structure that will be executable on multiple Windows operating system versions, service packs, and patch levels.
The way that we dynamically find each function address is by initially creating a hash of the function name that we want to call. The shellcode then searches through the relevant DLL (initially Kernel32.dll), calculating a hash for each function name that we discover, and comparing that hash with our pre-calculated hash.

Our Aim
The aim of our shellcode will be to create a hash generation program that takes a list of function name strings and spits out a list of corresponding hashes onto the stack. This list will be viewed using OllyDbg debugger.
This program will also demonstrate a little more structure where we define functions and constants.
We are compiling this assembly code as if it was shellcode (just for kicks); however, it doesn't really have any functionality as shellcode and is really just a really useful program used to create function hashes for use in other shellcode.
The next tutorial (Tutorial 6) will use the function hashes that we generate in this tutorial to convert the adduser.asm code from Tutorial 3, which has hardcoded function addresses, to completely dynamic shellcode that should work on all service packs and patch levels for Windows 98, Windows NT, Windows 2000, Windows XP, and Windows 2003.
From Tutorial 3 we used the functions "WinExec" and "ExitProcess", so we will be creating the hashes for these functions for use in Tutorial 6, along with a bunch of others that are commonly used.

What to look out for in the code
The assembly code below is structured with assembly functions and constants. These are shown by the "FUNCTION: function_name" and "DEFINE CONSTANTS" sections.
Functions are helpful when creating shellcode because they allow easy code reuse, which can help reduce the size of your shellcode when you use the functions more than once. They allow you to also easily copy them into other shellcode that you create, making future shellcode easier to write. Just be careful when creating shellcode that needs to be as small as possible that using a function isn't creating more code than just doing the action inline (as we have done for get_current_address ... but that's besides the point).
Functions defined using a label such as "get_current_address:", and are called by using the "call" operation, such as "call get_current_address". When the call operation is used it first pushes the current address to the stack so that it knows where to return to. The function is returned to the original calling position by using the "ret" operation.
Some functions will take "parameters" as inputs. These parameters may simply be that the function expects a certain value to be located within a specific register. However, quite often, parameters are passed to functions by pushing the values onto the stack using the "push" operation, such as "push eax". Multiple parameters may be passed to a function by pushing multiple values to the stack. The function will then grab these parameter values off the stack by using the "pop" operation, such as "pop ecx".
Functions may also push return values onto the stack so that the calling program can then pop the return values off. It is often found that return values are left in the "eax" register.
Constants are defined using the "db" operation, which simply writes the raw bytes of the string into the code, as shown below. The "db 0x00" writes a null byte at the end to terminate the string. We can then use a label to locate these bytes to use them as a complete string.
locate_constants: ;label start of constants
    db "WinExec"
    db 0x00
The assembly also has a number of "int 3" operations scattered throughout. "int 3" is called a "break point" that tells a debugger to stop at that instruction. This allows you to see the code being executed, the register states, the stack state, as well as being able to search through memory, etc.
The first break point is located at the start of the main program. This allows you to step through the code line by line from start to end, which is a great way to see exactly what each instruction is doing.
You will notice that I have put two break points in a row. OllyDbg often shows the current instruction at the top of the window, which means that if we only had one break point, "int 3" wouldn't be displayed since it had already been executed. This may be confusing for a newbie to OllyDbg, so I have put two break points so that the second "int 3" is shown to the user when the first break point is triggered.
The second set of break points stops the execution of the program after each hash has been created and pushed to the stack. This allows you to hit "play" and see each string and hash being pushed to the stack.
The third set of break points is at the very end of the program. This allows us to see the stack containing the resulting function hashes and corresponding function strings.

The Shellcode
The following is the code for hash-generator.asm.
Create hash-generator.asm on your local system within your Cygwin shellcode directory using the following code. Make sure you read the comments throughout the code since they explain what action each line performs, and gives handy tips for later shellcode development.
+--------------- Start hash-generator.asm --------------+
;hash-generator.asm
[SECTION .text]
BITS 32
global _start
_start:
jmp start_asm
;DEFINE FUNCTIONS
;FUNCTION: get_current_address
get_current_address:
    push 0 ;create a spot for our result
    push eax ;save eax value
    mov eax, [esp+8] ;copy the return address into eax
    mov dword [esp+4], eax ;move return address into result spot
    pop eax ;restore original eax value
    ret ;return to instruction that called this function
;END FUNCTION: get_current_address
;FUNCTION: compute_hash
compute_hash:
    push 0 ;create an empty spot for our result
    pushad ;save current registers onto stack
    mov eax, [esp+36] ;copy the return address into eax
    mov dword [esp+32], eax ;move return address into our empty spot.
     ;orig return addr spot will be our result spot
    xor edi, edi ;edi will hold our hash result
    xor eax, eax ;eax holds our current char
    cld
compute_hash_again:
    lodsb ;puts current char into eax
    test al, al ;checks for null - end of function string
    jz compute_hash_finished
    ror edi, 0xd ;rotate the current hash
    add edi, eax ;adds current char to current hash
    jmp compute_hash_again
compute_hash_finished: ;end of compute hash function
     ;edi now holds hash in 'reverse' order
    mov edx, edi ;move the result hash into edx
reverse_next_hash_section:
    mov al, dl ;move the first 8 bits into lower part of eax
    shr edx, 8 ;shift edx right to align next 8 bits of hash
    test dl, dl ;check for null - finished hash reversal
    jz reverse_hash_finished
    shl eax, 8 ;shift eax left ready for next hash section
    jmp short reverse_next_hash_section ; loop back to move next section
reverse_hash_finished: ;final hash is now in eax in correct order
    mov dword [esp+36], eax ;move return value into our return spot.
    popad ;restore the original register values
    ret ;return to instruction that called this function
;END FUNCTION: compute_hash
;DEFINE CONSTANTS
locate_constants: ;label start of constants
    call get_current_address ;find current location in memory
    pop esi ;esi is pointer to function strings
    add esi, 9 ;move pointer over these commands
    jmp short locate_constants_return ;return to our main code
    ;Function String
    db "LoadLibraryA" ;result hash = 0x8e4e0eec
    db 0x00
    db "WriteFile" ;result hash = 0x1f790ae8
    db 0x00
    db "CloseHandle" ;result hash = 0xfb97fd0f
    db 0x00
    db "Sleep" ;result hash = 0xb0492ddb
    db 0x00
    db "ReadFile" ;result hash = 0x1665fa10
    db 0x00
    db "GetStdHandle" ;result hash = 0x23d88774
    db 0x00
    db "CreatePipe" ;result hash = 0x808f0c17
    db 0x00
    db "SetHandleInformation" ;result hash = 0x44119e7f
    db 0x00
    db "WinExec" ;result hash = 0x98FE8A0E
    db 0x00
    db "ExitProcess" ;result hash = 0x7ED8E273
    db 0x00
    ;Null to indicate end of list
    db 0x00
;END DEFINE CONSTANTS
start_asm:
    int 3 ;start of main program
    int 3 ;second int 3 purely just to show up in OllyDbg
    jmp locate_constants ;find starting location of constants
locate_constants_return: ;define where to return after locating constants
next_hash: ;marks the start of the loop for next hash
    push esi ;push esi as parameter to compute_hash function
    call compute_hash ;compute_hash(esi_string)
     ;result now located in first position on stack
    int 3 ;tell debugger to stop for each hash created
    int 3 ;second int 3 purely just to show up in OllyDbg
    xor eax,eax ;clear eax
check_null: ;moves pointer to start of next function string
    lodsb ;puts current char into eax
    test al,al ;test if we point to a null
    jz is_null ;if we found a null, we reached end of string
    jmp short check_null ;loop back and check next char
is_null:
    lodsb ;puts current char into eax
    dec esi ;move it back one spot
    test al,al ;test if we point to a null
    jnz next_hash ;2 nulls means end, else loop back for next hash
end:
    int 3 ;calculated function hashes listed on stack
    int 3 ;tell debugger to stop to show hashes on stack
    int 3 ;second int 3 purely just to show up in OllyDbg
+--------------- End hash-generator.asm --------------+

Compiling the Assembly Code
So now that we have our assembly, we need to compile it. This can be done using the nasm assembly compiler, using the following command where hash-generator.asm is your assembly source code file, and hash-generator.bin is the compiled binary output file:
    # nasm -f bin -o hash-generator.bin hash-generator.asm

Obtaining the shellcode
Now that we have a compiled binary file we can use the xxd tool to generate the shellcode for us. This can be done using the following xxd command, which will generate the following output:
    # xxd -i hash-generator.bin
    unsigned char hash_generator_bin[] = {
     0xe9, 0xc7, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x50, 0x8b,
     0x44, 0x24, 0x08, 0x89, 0x44, 0x24, 0x04, 0x58, 0xc3, 0x68, 0x00, 0x00,
     0x00, 0x00, 0x60, 0x8b, 0x44, 0x24, 0x24, 0x89, 0x44, 0x24, 0x20, 0x31,
     0xff, 0x31, 0xc0, 0xfc, 0xac, 0x84, 0xc0, 0x74, 0x07, 0xc1, 0xcf, 0x0d,
     0x01, 0xc7, 0xeb, 0xf4, 0x89, 0xfa, 0x88, 0xd0, 0xc1, 0xea, 0x08, 0x84,
     0xd2, 0x74, 0x05, 0xc1, 0xe0, 0x08, 0xeb, 0xf2, 0x89, 0x44, 0x24, 0x24,
     0x61, 0xc3, 0xe8, 0xb6, 0xff, 0xff, 0xff, 0x5e, 0x81, 0xc6, 0x09, 0x00,
     0x00, 0x00, 0xeb, 0x7b, 0x4c, 0x6f, 0x61, 0x64, 0x4c, 0x69, 0x62, 0x72,
     0x61, 0x72, 0x79, 0x41, 0x00, 0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69,
     0x6c, 0x65, 0x00, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x48, 0x61, 0x6e, 0x64,
     0x6c, 0x65, 0x00, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x00, 0x52, 0x65, 0x61,
     0x64, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x47, 0x65, 0x74, 0x53, 0x74, 0x64,
     0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x00, 0x43, 0x72, 0x65, 0x61, 0x74,
     0x65, 0x50, 0x69, 0x70, 0x65, 0x00, 0x53, 0x65, 0x74, 0x48, 0x61, 0x6e,
     0x64, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69,
     0x6f, 0x6e, 0x00, 0x57, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x00, 0x45,
     0x78, 0x69, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x00, 0x00,
     0xcd, 0x03, 0xe9, 0x77, 0xff, 0xff, 0xff, 0x56, 0xe8, 0x3c, 0xff, 0xff,
     0xff, 0xcd, 0x03, 0x31, 0xc0, 0xac, 0x84, 0xc0, 0x74, 0x02, 0xeb, 0xf9,
     0xac, 0x4e, 0x84, 0xc0, 0x75, 0xe9, 0xcd, 0x03, 0xcd, 0x03
    };
    unsigned int hash_generator_bin_len = 238;
This creates an array of characters that can be used within a C program. Each of the hexadecimal numbers (0xXX) in the output represent a byte within the shellcode.
So we want to do with this output to create the shellcode? We are going to use the xxd-shellcode.sh script from a previous tutorial to strip out the raw shellcode that we can put directly into our "shellcodetest.c" program.
This program takes the input file "hash-generator.bin", so run the following command to produce the following output. This output will automatically be saved to "hash-generator.shellcode". Thisshellcode may vary slightly if you are using a different OS or service pack due to the differing address of the functions we call on your system.
    # ./xxd-shellcode.sh hash-generator.bin
    \xe9\xc7\x00\x00\x00\x68\x00\x00\x00\x00\x50\x8b\x44\x24\x08\x89\x44\x24\x04\x58\xc3\x68\x00\x00\x00\x00\x60\x8b\x44\x24\x24\x89\x44\x24\x20\x31\xff\x31\xc0\xfc\xac\x84\xc0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb\xf4\x89\xfa\x88\xd0\xc1\xea\x08\x84\xd2\x74\x05\xc1\xe0\x08\xeb\xf2\x89\x44\x24\x24\x61\xc3\xe8\xb6\xff\xff\xff\x5e\x81\xc6\x09\x00\x00\x00\xeb\x7b\x4c\x6f\x61\x64\x4c\x69\x62\x72\x61\x72\x79\x41\x00\x57\x72\x69\x74\x65\x46\x69\x6c\x65\x00\x43\x6c\x6f\x73\x65\x48\x61\x6e\x64\x6c\x65\x00\x53\x6c\x65\x65\x70\x00\x52\x65\x61\x64\x46\x69\x6c\x65\x00\x47\x65\x74\x53\x74\x64\x48\x61\x6e\x64\x6c\x65\x00\x43\x72\x65\x61\x74\x65\x50\x69\x70\x65\x00\x53\x65\x74\x48\x61\x6e\x64\x6c\x65\x49\x6e\x66\x6f\x72\x6d\x61\x74\x69\x6f\x6e\x00\x57\x69\x6e\x45\x78\x65\x63\x00\x45\x78\x69\x74\x50\x72\x6f\x63\x65\x73\x73\x00\x00\xcd\x03\xe9\x77\xff\xff\xff\x56\xe8\x3c\xff\xff\xff\xcd\x03\x31\xc0\xac\x84\xc0\x74\x02\xeb\xf9\xac\x4e\x84\xc0\x75\xe9\xcd\x03\xcd\x03

Creating an executable with the shellcode
We will be using the "shellcodetest.c" program to test our shellcode. The aim of this step is to insert our shellcode into a C program, which we can then compile and execute. This program is designed to then execute our shellcode.
Before we can do this you need to insert your shellcode that was generated from the last step into this program. You place it between the quotes of the "code[]" array. It should end up looking something like the following:
+----------------- Start updated shellcodetest.c -----------------+
/*shellcodetest.c*/
char code[] = "\xe9\xc7\x00\x00\x00\x68\x00\x00\x00\x00\x50\x8b\x44\x24\x08\x89\x44\x24\x04\x58\xc3\x68\x00\x00\x00\x00\x60\x8b\x44\x24\x24\x89\x44\x24\x20\x31\xff\x31\xc0\xfc\xac\x84\xc0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb\xf4\x89\xfa\x88\xd0\xc1\xea\x08\x84\xd2\x74\x05\xc1\xe0\x08\xeb\xf2\x89\x44\x24\x24\x61\xc3\xe8\xb6\xff\xff\xff\x5e\x81\xc6\x09\x00\x00\x00\xeb\x7b\x4c\x6f\x61\x64\x4c\x69\x62\x72\x61\x72\x79\x41\x00\x57\x72\x69\x74\x65\x46\x69\x6c\x65\x00\x43\x6c\x6f\x73\x65\x48\x61\x6e\x64\x6c\x65\x00\x53\x6c\x65\x65\x70\x00\x52\x65\x61\x64\x46\x69\x6c\x65\x00\x47\x65\x74\x53\x74\x64\x48\x61\x6e\x64\x6c\x65\x00\x43\x72\x65\x61\x74\x65\x50\x69\x70\x65\x00\x53\x65\x74\x48\x61\x6e\x64\x6c\x65\x49\x6e\x66\x6f\x72\x6d\x61\x74\x69\x6f\x6e\x00\x57\x69\x6e\x45\x78\x65\x63\x00\x45\x78\x69\x74\x50\x72\x6f\x63\x65\x73\x73\x00\x00\xcd\x03\xe9\x77\xff\xff\xff\x56\xe8\x3c\xff\xff\xff\xcd\x03\x31\xc0\xac\x84\xc0\x74\x02\xeb\xf9\xac\x4e\x84\xc0\x75\xe9\xcd\x03\xcd\x03";
int main(int argc, char **argv)
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}
+----------------- End updated shellcodetest.c -----------------+
We now need to compile the updated shellcodetest.c program so that it can kick off our shellcode. Usually this would be done with gcc within Cygwin; however, this time we want to run the executable from within Windows itself. So instead of using gcc within Cygwin we are going to use the "lcc-win32" Windows C compiler that we installed in Tutorial 1.
You can start LCC from "Start->All Programs->lcc-win32->lcc-win32". You may need to create a new project, so create a project called "hash_generator" or something from the File menu. If it prompts you to create an "application skeleton" select no, since we already have our C code and we just want to compile it. Follow the rest of the Wizards through with default options.
Once it has started you want to use the File menu to open shellcodetest.c, which would be located at a location something like "C:\cygwin\home\{username}\shellcode\shellcodetest.c".
When you open this file LCC will probably give you a warning saying "1 lines longer than 500 characters were truncated". Just ignore this. It is just our long shellcode line that is being truncated from being displayed - its not actually truncating our shellcode.
To compile this code, go to the "Compile" menu and select "Compile shellcodetest.c". You should now have a project directory called "lcc" within your shellcode directory, which should now contain shellcodetest.exe.
Usually we would simply execute this program to kick off our shellcode; however, this time it is useless to do this because we want to see our resulting hashes being generated on the stack. We do this by using the OllyDbg debugger.

Executing and analyzing shellcode with OllyDbg
You can start OllyDbg by simply double-clicking the executable within the zip file downloaded in Tutorial 1. Once it has started you want to use the File menu to open shellcodetest.exe, which would be located at a location something like "C:\cygwin\home\{username}\shellcode\lcc\shellcodetest.exe".
The following figure shows you what you should see once you have opened your executable with OllyDbg, along with some quick pointers of what you are looking at.
The above figure shows the executable loaded into OllyDbg.
The top left window shows the instructions that are going to be executed on the machine. Break points can be set on any instruction in real-time by double clicking any address on the far left in this window, which would show up in red. Double clicking it again cancels the break point. Any instruction can be changed in real-time by double clicking on the instruction itself.
The top right window shows the registers and the values that they hold. This allows you to easily see exactly what the instructions are doing on the registers. If the register changes after an instruction it is highlighted in red.
The bottom right window is the stack. This allows you to see what is being pushed, stored, and popped on the stack in real time. This is awesome for understanding exactly how the stack is manipulated. Once you get a clear understanding of this it will help you understand how to write more efficient shellcode.
The bottom left window is a memory dump, which allows you to see and search for raw bytes in memory. You can click on any register, stack, or code address and select "Show in dump".
The "Play" button starts the program running and will keep running until it reaches either a break point, an access violation, or the end of the program. This can also be done by hitting "F9".
The "Step Into" button will execute one instruction at a time. If you reach a "call" instruction it will also step into the function and execute each instruction one by one also. This can also be done by hitting "F7".
The "Step Over" button will execute one instruction at a time also; however, if it reaches a "call" instruction it will "step over" the function, meaning that it will execute the function but won't show you every instruction.
For this tutorial, we are going to hit the "Play" (or F9) button to start our program. Since we have "int 3" instructions (break points) coded into our shellcode, the debugger will stop executing at the first break point it reaches. This is shown in the following figure:
The figure above shows one of our break points (int 3). This is the start of the main section of ourshellcode that we created above. If you wanted to see exactly what happens to each register and to the stack for each instruction throughout the program you could keep hitting the "Step Into" (F7) or "Step Over" (F8) buttons.
Remember that we had sets of two break points in a row in our code. This is so that when OllyDbg stops after the first "int 3" it would still show one "int 3", as shown in the figure above, so that we knew immediately that it stopped because of the break point. Otherwise if it stopped due to an error then an error message would appear in the status bar at the bottom of OllyDbg.
If you want to start the program executing again then hit the Play button twice (since we have two break points). This will take you to the next set of break points that we coded into our program, which are located after the function hash is calculated and pushed to the stack.
This time we can see the two break points in the clear. The call instruction before the break points is the "call compute_hash" instruction. This function calculates the hash and pushes it to the stack. You can see this in the "Stack Window" where the function string has been pushed to the stack, followed by the calculated function hash.
You need to remember that the stack is like stacking up a deck of cards. The last card pushed onto the stack is the one on top, and is the first to be popped off the stack. You can also manipulate where the top of the stack is by changing the "esp" (extended stack pointer) register.
So we can see that the first function string has been found, pushed to the stack, and the corresponding hash has been calculated and pushed to the stack. If you now hit Play twice then you will see the next function name and hash get pushed onto the stack. If you keep doing this you should start to see something like the following, where a list of function names and hashes are located on the stack.
If you keep hitting Play you will eventually reach the end of the function name list and the program will jump down to the end, as shows by the three break points. This allows you to see the final list of function names and hashes, as shown below:

Congratulations!
You have just created a hash generation program that defines, locates, and uses a list of function name strings and spits out a list of corresponding hashes onto the stack. You have learned the basics of OllyDbg by executing the program within OllyDbg, allowing you to see the registers and stack in the clear.
You have also learned how you can use a more structured approach when creating shellcode, allowing code reuse and more efficient shellcode.
You will use this program over and over again when creating shellcode as you find you need to call new Windows functions and need the corresponding hashes. This is the first step in creating genericshellcode that can be used across different Windows operating systems.
Tutorial 6 will show you how to do exactly this! We will change the adduser.asm code to remove the hardcoded addresses. We will therefore need the function hashes generated in this tutorial for functions "WinExec" and "ExitProcess".
Enjoy!

  • Blogger Comments
  • Facebook Comments

2 comments:

  1. Do you need to increase your credit score?
    Do you intend to upgrade your school grade?
    Do you want to hack your cheating spouse Email, whatsapp, Facebook, instagram or any social network?
    Do you need any information concerning any database.
    Do you need to retrieve deleted files?
    Do you need to clear your criminal records or DMV?
    Do you want to remove any site or link from any blog?
    you should contact this hacker, he is reliable and good at the hack jobs..
    contact : cybergoldenhacker at gmail dot com

    ReplyDelete
  2. Are you willing to know who your spouse really is, if your spouse is cheating just contact cybergoldenhacker he is good at hacking into cell phones,changing school grades and many more this great hacker has also worked for me and i got results of spouse whats-app messages,call logs, text messages, viber,kik, Facebook, emails. deleted text messages and many more this hacker is very fast cheap and affordable he has never disappointed me for once contact him if you have any form of hacking problem am sure he will help you THANK YOU.
    contact: cybergoldenhacker at gmail dot com

    ReplyDelete

Item Reviewed: Shellcode Tutorial 5: Function Hash Generation Rating: 5 Reviewed By: Unknown