/*
 * purpose: find specific library in process space
 */
#include <stdlib.h>
#include <stdio.h>

int find_library(const char *name);
int find_function(const char *function);

int main(int argc, char **argv)
{
//	printf("va addr for '_dl_starting_up' is: %.8x\n", find_function("_dl_unload_cache"));
//	printf("va addr for 'system' is: %.8x\n", find_function("system"));
//	printf("va addr for 'writez' is: %.8x\n", find_function("write"));

#ifdef FUNCTION
	if (argc > 1)
	{
		printf("va for '%s' is: ", argv[1]);
		fflush(stdout);

		printf("%.8x\n", find_function(argv[1]));
	}
#else
	if (argc > 1)
	{
		printf("va for '%s' is: ", argv[1]);
		fflush(stdout);

		printf("%.8x\n", find_library(argv[1]));
	}
#endif

//		printf("va addr for '%s' is: %.8x\n", argv[1], find_function(argv[1]));
		

//	find_library("ld-linux.so.2");

	return 0;
}

int find_library(const char *name)
{
	int ret = -1;

	do
	{
		/*
		 * library level:
		 *
		 * 	walk page by page testing for ELF signature
		 * 	if match elf signature
		 * 		walk page by page until invalid addr is hit or until ELF signature is hit
		 * 		once hit do the following
		 * 			find section table offset in elf header
		 * 			walk section table for .dynamic section
		 * 			look for SONAME entry in .dynamic
		 * 			if matches requested library, then we have a match
		 *
		 * dragnet R dumb but he still try to code dis one :D:D:D:D::D:D:D::D:DD:D:D:D:D			
		 */
		__asm__ __volatile__
		(
 	 		"	jmp fl_startup\n"
			"\n"
		 	"fl_find_dynamic_entry:\n"              // Takes eax as dynamic base, desired tag in ecx, ebx = library base
			"	push %%eax\n"                        // Save eax for caller
			"	push %%esi\n"                        // Save esi for caller
			"	mov %%eax, %%esi\n"                  // Set esi to the start of the dynamic table
			"fl_find_dynamic_entry_loop:\n"          
			"	lodsl\n"                             // Load d_tag into eax
			"	test %%eax, %%eax\n"                 // Test eax to see if it's null
			"	jz fl_find_dynamic_entry_fail\n"     // We hit DT_NULL
			"	cmpl %%ecx, %%eax\n"                 // Compare d_tag to the tag passed in
			"	lodsl\n"                             // Load the value for the tag into eax
			"	jnz fl_find_dynamic_entry_loop\n"    // Loop back through if the tag doesn't match what we're looking for
			"fl_find_dynamic_entry_end:\n"
			"	mov %%eax, %%edx\n"                  // Set edx to the value of the tag searched for
			"fl_find_dynamic_entry_fail:\n"         // Jump here if we fail to find the entry
			"	pop %%esi\n"                         // Restore esi
			"	pop %%eax\n"                         // Restore eax
			"	ret\n"                               // Ret to caller
			"\n"
			"fl_startup:\n"
			"	xor %%ebx, %%ebx\n"                  // Zero ebx
			"fl_check_page_loop:\n"
			"	xor %%ecx, %%ecx\n"                  // Zero ecx
			"	xor %%eax, %%eax\n"                  // Zero eax
			"	mov $0x21, %%al\n"                   // Set eax to 0x21 (NR_access)
			"	int $0x80\n"                         // Syscall
			"	cmpb $0xf2, %%al\n"                  // Did it fail from bad addr?
			"	jne fl_check_elf_sig\n"              // If it didn't fail for that reason, check the elf signature
			"fl_check_page_loop_cont:\n"
			"	addw $0x0fff, %%bx\n"                // Increment our base address by PAGESIZE + 1 to check the next page. +1 eliminates null
			"	inc %%ebx\n"                         // Decrement ebx by one to get back to a page boundary
			"	jmp fl_check_page_loop\n"            // Jump back through the page checking loop
			"fl_check_elf_sig:\n"
			"	cmpl $0x464c457f, (%%ebx)\n"         // Do the first four bytes of the header match the elf signature?
			"	jne fl_check_page_loop_cont\n"       // If not, loop through again starting at the next page
			"fl_check_header_type:\n"	
			"	cmpb $0x3, 16(%%ebx)\n"              // Check to see if e_type is set to ET_DYN 
			"  jne fl_check_page_loop_cont\n"       // Nope, loop through next
			"\n" // ebx = base
			"fl_find_dynsym:\n"
			"	mov %%ebx, %%esi\n"                  // Set esi to the base of the library
			"	mov 28(%%ebx), %%eax\n"              // Set eax to the program header offset
			"	add %%eax, %%esi\n"                  // Add the offset to esi
			"fl_walk_program_headers:\n"   
			"fl_walk_program_headers_loop:\n"
			"	cmpb $0x2, (%%esi)\n"                // Compare p_type to PT_DYNAMIC
			"  je fl_walk_dynamic\n"                // If it matches PT_DYNAMIC, start looking at it
			"fl_walk_program_headers_cont:\n"
			"	add $0x20, %%esi\n"                  // Go to the next entry in the table 
			"	jmp fl_walk_program_headers_loop\n"  // Go through to the next program header 
			"fl_walk_dynamic:\n"
			"	mov 4(%%esi), %%eax\n"               // Store the file offset in eax (may need to use vaddr instead)
			"	add %%ebx, %%eax\n"                  // Make the address absolute
			"fl_get_strtab:\n"
			"	xor %%ecx, %%ecx\n"                  // Zero ecx
			"	mov $0x5, %%cl\n"                    // Set ecx to DT_STRTAB
			"	call fl_find_dynamic_entry\n"        // Get it
			"	mov %%edx, %%esi\n"                  // Set the value to eax
			"	test %%edx, %%edx\n"
			"	jz fl_check_page_loop_cont\n"        // If the zero flag is set from the previous test, we know we failed.
			"fl_get_soname:\n"
			"	mov $0xe, %%cl\n"                    // Set ecx to DT_SONAME 
			"	call fl_find_dynamic_entry\n"        // Look up the value for DT_SONAME
			"	add %%edx, %%esi\n"                  // Save the string pointer in esi
			"	test %%edx, %%edx\n"
			"	jz fl_check_page_loop_cont\n"        // If the zero flag is set from the previous test, we know we failed.
			"	cmp %%esi, %%ebx\n"                  // Compare the dynamic address with the base address of the library
			"	jle fl_skip_absolution\n"            // Skip over making it absolute if it already is
			"	add %%ebx, %%esi\n"                  // Absolute
			"fl_skip_absolution:\n"
			"	mov %0, %%edi\n"                     // Set edi to the library being searched for
			"	xor %%eax, %%eax\n"                  // Zero eax
			"	xor %%ecx, %%ecx\n"                  // Zero ecx
			"	not %%ecx\n"                         // Invert bits on ecx
			"	push %%edi\n"                        // Save edi because scasb will modify it
			"	repnz scasb\n"                       // Repeat count edi
			"	pop %%edi\n"                         // Restore edi
			"	not %%ecx\n"                         // Revert bytes in ecx to get string length + 1
			"	dec %%ecx\n"                         // Decrement to get the real string length
			"	repz cmpsb\n"                        // Repeat comparing edi to esi
			"	movb (%%edi), %%al\n"                // Move the current byte in edi into al
			"	test %%al, %%al\n"                   // Test for zero
			"	jnz fl_check_page_loop_cont\n"       // Go to the next library
			"fl_found_lib:\n"
			"	mov %%ebx, %1\n"                     // Set ret to the base of this library
			:
			: "m"((long)name), "m"((long)ret)
			: "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"
		);

	} while (0);

	return ret;
}
		 

int find_function(const char *function)
{
	int ret = -1;

	do
	{
		/*
		 * function level:
		 *
		 * 	walk page by page testing for ELF signature
		 * 	verify that it's an elf library
		 * 	if signature matches, walk program header table
		 * 	if program header entry is PT_DYNAMIC, inspect the contents of it
		 * 	look for DT_SYMTAB and DT_STRTAB in the dynamic section
		 * 	walk the symbol table base off the DT_SYMTAB offset until you run into the strtab
		 * 	compare the value name of the symbol with the one being searched for
		 */
		__asm__ __volatile__(
			"	jmp startup\n"
			"\n"
			"find_dynamic_entry:\n"              // Takes eax as dynamic base, desired tag in ecx, ebx = library base
			"	push %%eax\n"                     // Save eax
			"find_dynamic_entry_loop:\n"          
			"	cmpl %%ecx, (%%eax)\n"            // Compare d_tag to the tag passed in
			"	je find_dynamic_entry_end\n"      // Found the entry
			"	add $0x8, %%eax\n"                // No match, go to the next entry
			"	jmp find_dynamic_entry_loop\n"    // Loop back through
			"find_dynamic_entry_end:\n"
			"	mov 4(%%eax), %%edx\n"            // Set edx to the value of the tag searched for
			"	pop %%eax\n"                      // Restore eax to the dynamic base
			"	ret\n"                            // Ret to caller
			"\n"
			"startup:\n"
			"	xor %%ebx, %%ebx\n"               // Zero ebx
			"check_page_loop:\n"
			"	xor %%ecx, %%ecx\n"               // Zero ecx
			"	xor %%eax, %%eax\n"               // Zero eax
			"	mov $0x21, %%al\n"                // Set eax to 0x21 (NR_access)
			"	int $0x80\n"                      // Syscall
			"	not %%eax\n"                      // Invert eax
			"	cmp $0xd, %%eax\n"                // Did it fail from bad addr?
			"	jne check_elf_sig\n"              // If it didn't fail for that reason, check the elf signature
			"check_page_loop_cont:\n"
			"	add $0x1000, %%ebx\n"             // Increment our base address by PAGESIZE to check the next page
			"	jmp check_page_loop\n"            // Jump back through the page checking loop
			"check_elf_sig:\n"
			"	cmpl $0x464c457f, (%%ebx)\n"      // Do the first four bytes of the header match the elf signature?
			"	jne check_page_loop_cont\n"       // If not, loop through again starting at the next page
			"check_header_type:\n"	
			"	cmpw $0x3, 16(%%ebx)\n"           // Check to see if e_type is set to ET_DYN 
			"  jne check_page_loop_cont\n"       // Nope, loop through next
			"\n"
			"find_dynsym:\n"               
			"	mov %%ebx, %%esi\n"               // Set esi to the base address of the library
			"  mov 28(%%ebx), %%eax\n"           // Get the e_phoff count
			"	add %%eax, %%esi\n"               // Offset esi into the program header table
			"walk_program_headers:\n"   
			"walk_program_headers_loop:\n"
			"	cmpb $0x2, (%%esi)\n"             // Compare p_type to PT_DYNAMIC
			"  je walk_dynamic\n"                // If it matches PT_DYNAMIC, start looking at it
			"walk_program_headers_cont:\n"
			"	addl $0x20, %%esi\n"              // Go to the next entry in the table 
			"	jmp walk_program_headers_loop\n"  // Go through to the next program header 
			"walk_dynamic:\n"
			"	mov 4(%%esi), %%eax\n"            // Store the file offset in eax (may need to use vaddr instead)
			"	add %%ebx, %%eax\n"               // Make the address absolute
			"find_dt_symtab:\n"
			"	mov $0x6, %%ecx\n"                // Set ecx to DT_SYMTAB
			"	call find_dynamic_entry\n"        // Find the entry
			"	mov %%edx, %%ecx\n"               // Save the base in eax
			"find_dt_strtab:\n"
			"	push %%ecx\n"                     // Save ecx
			"	mov $0x5, %%ecx\n"                // Set ecx to DT_STRTAB
			"	call find_dynamic_entry\n"        // Find the entry
			"	pop %%ecx\n"                      // Restore ecx
			"\n"
			// at this point ebx = library base, ecx = dynsym base, edx = dynstr base
			"walk_symbols:\n"              
			"walk_symbols_loop:\n"
			"	cmp %%ecx, %%edx\n"               // Make sure we haven't gone past into dynstr
			"	jle check_page_loop_cont\n"       // Start going to the next page to find a new library
			"	mov (%%ecx), %%eax\n"             // Set eax to the name of this symbol
			"	push %%eax\n"                     // Save eax
			"	mov 12(%%ecx), %%eax\n"           // Copy st_info and others into eax
			"	andl $0xf, %%eax\n"               // Mask off everything but the type
			"	dec %%eax\n"                      // Decrement eax
			"	dec %%eax\n"                      // Decrement eax
			"	test %%eax, %%eax\n"              // Test eax to see if it's zero, it should be if the type was STT_FUNC
			"	pop %%eax\n"                      // Restore eax
			"	jnz walk_symbols_cont\n"          // If it isn't zero, we jump to the next entry
			"	mov %%edx, %%esi\n"               // Set esi to the base address of the string table
			"	add %%eax, %%esi\n"               // Offset to where the name of the symbol is at
			"	mov %1, %%edi\n"                  // Set edi to the name of the symbol we're looking for
			"	xor %%eax, %%eax\n"               // Zero eax
			"	push %%ecx\n"                     // Save ecx so rep doesn't clobber it
			"	xor %%ecx, %%ecx\n"
			"	not %%ecx\n"
			"	push %%edi\n"
			"	repnz scasb\n"
			"	pop %%edi\n"
			"	not %%ecx\n"
			"	dec %%ecx\n"
			"	repz cmpsb\n"                     // Repeat comparing (%esi) to (%edi)
			"	pop %%ecx\n"                      // Restore ecx
			"	movb (%%edi), %%al\n"             // Move the byte in edi to al
			"	test %%al, %%al\n"                // Test to see if it's a null byte
			"	jz walk_symbols_found\n"          // If it is null, we know we found a match (sort of)
			"walk_symbols_cont:\n"
			"	add $0x10, %%ecx\n"               // Go to the next entry in the symbol table
			"	jmp walk_symbols_loop\n"          // Loop through
			"walk_symbols_found:\n"           
			"	mov 4(%%ecx), %%eax\n"            // Set eax to the st_value for the symbol
			"	add %%ebx, %%eax\n"               // Add the base address to the value
			"	mov %%eax, %0\n"                  // Save eax in ret
			: 
			: "m"((long)ret), "m"((long)function)
			: "%ebx", "%ecx", "%edx", "%esi", "%edi"
		);

	} while (0);

	return ret;
}

// i wanna do find_function first cuz it'll be more fun that find_library :P
