Jump to content
Tuts 4 You

Collection of direct import implementations


Aguila

Recommended Posts

I'm currently working on Scylla and I want to implement a direct import scanner. It would be nice if we could collect the different direct import implementations of protectors.


 


For example:


 


 


eXPressor


-------------


5 byte CALL 0xFFFFFFFF + 1 byte bogus value


 


 


Themida/Winlicense


-------------


5 byte JMP 0xFFFFFFFF + 1 byte bogus value


 


 


are there any more?


Link to comment

Themida (at least in some versions) will either prepend or append a nop to the 'jmp/call directImport'. This will lead to problems when considering jumps that lie within a nop sled. In such cases you cannot know for sure whether the nop before or after the jump was inserted by Themida and your replacement (jmp dword ptr [iatDirectImport]) might be off by one byte. You could examine xrefs but well..


 


Consider implementing a "fail-safe" option that builds a table containing "jmp dword ptr[iatDirectImport]" and merely fixes the direct import's offset to the corresponding entry in the table.


Link to comment

Hi,


 


UIF does also not work for 100 % in different cases of direct fixing commands.So if any direct reference is found of a command address then UIF will fix it there anf if no ref was found then it used other basic checks of nops or not nops [above & below] and takes then the decision where to fix it and this can fail.So if above of a command was found a nop byte and no refs was found then UIF will fix it at this nop above at -1.If above was no nop then it fix it at same address.So if no nops are used instead...



01001003 STOS DWORD PTR ES:[EDI]
01001004 CALL 7C80B741 ; kernel32.GetModuleHandleA
01001009 INT3 01001003   STOS DWORD PTR ES:[EDI]
01001004 CALL DWORD PTR DS:[10010C3]  ; kernel32.GetModuleHandleA
0100100A   NOP

...then it fixed also at the same address and overwrites the int3 command but what is if this is real code to force a exception so then it was fixed at the wrong address instead to use 01001003.Also it can happen that UIF overwrites before created commands by itself as in a jump table so that you later can see this.



01001000 JMP DWORD PTR DS:[10010C3] ; kernel32.GetModuleHandleA
01001006 AND EAX,10010C3
0100100B JMP DWORD PTR DS:[10010C3] ; kernel32.GetModuleHandleA

All in all its not so easy to create a solution which could work really for full 100 % but in the most cases a 100 % solution is also not necessary.So you could also use the UIF.dll with Scylla if you want but in UIF are also some tiny bugs which you need to fix.


 


PS: UIF does check refs via call,mov, etc,addr and if found then it fixed it at the found address where the direct API was found.Just play a little with UIF and write some own different commands and let fix it and you see the different results.


 


greetz


Link to comment

No, I don't want and I can't use UIF (because no x64 and not open source)


 


I just implemented a solution for the basic direct import:


http://forum.tuts4you.com/files/file/576-scylla-imports-reconstruction/


The junk byte must be after the CALL/JMP instruction.


 


Does a protector use various methods for direct imports in a packed executable? Or is it always the same method for all direct imports. If it is mostly the same for all, I could simply ask the unpacker about the method.


Link to comment

Ah ok.


Ok I have test your new Scylla quickly and at the moment it will make not much sens to use this direct fixing with your tool for all so you need to add much more checks to prevent possible fixing at the wrong addresses.So what is your goal?Do you want that Scylla can fix all direct imports?If yes then you also need to scan other commands too mov,lea,push,jump to jump etc which can also handle direct APIs.Also I see in your new Scylla that it only fix direct API commands to the entered IAT so what is if the API is not to find in the IAT then it will not fix it etc or create a alternativ IAT or just extra imports etc.


 


So if you want to add a almost full handling of all direct commands then you need to add more scan and verify operations and this could be take a longer time to write such code in your source.More options in your Scylla later would be also nice and result boxes too etc.


 


greetz


Link to comment

Aguila, if 'various' refers to the nop being before or after the jump, then yes. Else it's always the same for direct imports afaik (still speaking of TM). However this does not have to apply to all imports.


 


Side note: Themdia keeps the original IAT intact. Direct imports are added as an entry as well as redirections (stolen API code). The latter are usually also called directly in the code using the same nop padding. I guess TM does this in case it is not able to find all references to an IAT entry since scanning commands is not that safe.


  • Like 1
Link to comment

If yes then you also need to scan other commands too mov,lea,push,jump to jump etc which can also handle direct APIs.

This was my initial question. Are there any more commands I need to scan?

At least I want to find all possible direct imports. Maybe fixing it in a general way is too difficult, but at least finding them should be possible.

 

The direct import scanner is just some "side product" because I had to scan for IAT references for building a new IAT.

 

Thanks metr0 and LCF-AT for the information!

Link to comment

There are some opcodes to be taken into account, the ones that manage absolute addresses. That's the way as ARImpRec.dll gets the IAT valid handles of imported functions. In my tool, any handle not being referenced by code is ignored as a valid one (at least in one of its different ways of working)...


Link to comment

Hi,


 


so I see your new version does find direct mov API commands but does not fix them also if this API is in the IAT to find only jmp & call are working.If the API is not in IAT then it will also find direct commands and ask you about fixing Yes | No but it does not fix it so better you check this again later.


 


So what you can try to do is to create a more advanced tool for this direct scan & fix issue where at the end the user has different choices what the user wanna do.


 


1.) Scan for all possible direct API commands jmp / call / push / mov / lea / pop


2.) Log all founds for you


3.) Scan for possible direct refs of the founds & refs of -1 



010075BA STOS DWORD PTR ES:[EDI]
010075BB JMP 77C05C94 ; msvcrt._except_handler3 Found ref of 010075BB = nothing
Found ref of 010075BB -1 = 010075BA = 1 found ref = fix there
01007568 PUSH 10075BA

So if you can find any ref then you can find them on that way.If you can't find any ref then you use some different static check pattern at the found address to find the possible fix address = same or above.


 


Also you could add different fixing methods like direct to direct fixings if you just patch the address of the found API command with a jump to your own code = new section where you move or call the API and then jump back again.Looks not very beautiful but its a good working alternativ if you really not know where to fix any xy direct command and before to choose just a random address you could use this way.So in my latest TM WL script I addedd this alternativ feature too only for direct API jumps but you could add this for all of course.


 


So you can let fix all direct API commands with the entered IAT data by the user but if you found direct API commands where the API was not found in IAT then you can add this into a 2. Scylla IAT block into Scylla section.


 


So all in all there are many features which you could add so create at the end a advanced tool so that also the user don't need to choose whether to fix about the junk bytes etc . :)


 


PS: In your log file you could also add API names later not only the addresses.Also you could add a another window box in Scylla where you let can show / list the direct founds + disassember view would also be nice.More features = more fun.


 


greetz


Link to comment

Also you could add different fixing methods like direct to direct fixings if you just patch the address of the found API command with a jump to your own code = new section where you move or call the API and then jump back again.Looks not very beautiful but its a good working alternativ if you really not know where to fix any xy direct command and before to choose just a random address you could use this way.So in my latest TM WL script I addedd this alternativ feature too only for direct API jumps but you could add this for all of course.

 

You are right, this is probably the best solution. I think it is a really nice solution and I think this will work for x64 too. I am always looking for a perfect solution for x86 and x64... otherwise it doesnt make sense for me to implement it.

 

I didn't add a mov fixer yet.

Can you show me an example of push / mov / lea / pop? I mean a real example from a protector. Is this used somewhere?

 

Thanks for your suggestions.

Link to comment

So I don't remember anymore which protector used this or that feature.So at the moment you can use all possible direct commands which can be used (without lea & pop = no directs possible).


 


So I made a small exsample script how it could look later with method 1 and method 2.Just run it so maybe it help you to get a quick overview if you need it.



pause
gpa "GetModuleHandleA", "kernel32.dll"
mov GPA, $RESULT
alloc 1000
mov SEC, $RESULT
mov [SEC], GPA
alloc 1000
mov eip, $RESULT
mov SEC2, $RESULT
pusha
mov eax, SEC2
///////////////////////////
MOV_5:
eval "mov eax, {GPA}"
asm eax, $RESULT
call ADD6
eval "mov ecx, {GPA}"
asm eax, $RESULT
call ADD6
eval "mov edx, {GPA}"
asm eax, $RESULT
call ADD6
eval "mov ebx, {GPA}"
asm eax, $RESULT
call ADD6
eval "mov ebp, {GPA}"
asm eax, $RESULT
call ADD6
eval "mov esi, {GPA}"
asm eax, $RESULT
call ADD6
eval "mov edi, {GPA}"
asm eax, $RESULT
call ADD6
///////////////////////////
MOV_6:
eval "mov eax, {GPA}"
asm eax, $RESULT, 1
call ADD6
mov [eax-1], 90, 01
eval "mov ecx, {GPA}"
asm eax, $RESULT, 1
call ADD6
mov [eax-1], 90, 01
eval "mov edx, {GPA}"
asm eax, $RESULT, 1
call ADD6
mov [eax-1], 90, 01
eval "mov ebx, {GPA}"
asm eax, $RESULT, 1
call ADD6
mov [eax-1], 90, 01
eval "mov ebp, {GPA}"
asm eax, $RESULT, 1
call ADD6
mov [eax-1], 90, 01
eval "mov esi, {GPA}"
asm eax, $RESULT, 1
call ADD6
mov [eax-1], 90, 01
eval "mov edi, {GPA}"
asm eax, $RESULT, 1
call ADD6
mov [eax-1], 90, 01
///////////////////////////
PUSH_5:
eval "push {GPA}"
asm eax, $RESULT
call ADD6
mov [eax-1], 90, 01
///////////////////////////
CALL_5:
eval "call {GPA}"
asm eax, $RESULT
call ADD6
mov [eax-1], 90, 01
///////////////////////////
JMP_5:
eval "jmp {GPA}"
asm eax, $RESULT
call ADD6
mov [eax-1], 90, 01
msg "Look Code then Resume Script!"
pause
msgyn "Choose Fix Method 1 = Normal Press YES! \r\n\r\nChoose Fix Method 2 = Custom Press NO!"
cmp $RESULT, 01
je NORMAL
///////////////////////////
CUSTOM:
mov eax, SEC2
mov esi, SEC2
add esi, 6A
mov edi, SEC
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov eax, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
mov [eax-1], 90, 01
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov ecx, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
mov [eax-1], 90, 01
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov edx, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
mov [eax-1], 90, 01
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov ebx, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
mov [eax-1], 90, 01
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov ebp, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
mov [eax-1], 90, 01
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov esi, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
mov [eax-1], 90, 01
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov edi, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
mov [eax-1], 90, 01
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
///////////////////////////
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov eax, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov ecx, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov edx, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov ebx, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov ebp, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov esi, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "mov edi, [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "push [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "call [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
eval "jmp {eax}"
asm esi, $RESULT, 1
add esi, 05
eval "jmp {esi}"
asm eax, $RESULT, 1
eval "jmp [{edi}]"
asm esi, $RESULT
add esi, 06
call ADD6
popa
ret
///////////////////////////
NORMAL:
mov eax, SEC2
mov edi, SEC
eval "mov eax, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov ecx, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov edx, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov ebx, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov ebp, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov esi, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov edi, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov eax, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov ecx, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov edx, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov ebx, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov ebp, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov esi, [{edi}]"
asm eax, $RESULT
call ADD6
eval "mov edi, [{edi}]"
asm eax, $RESULT
call ADD6
eval "push [{edi}]"
asm eax, $RESULT
call ADD6
eval "call [{edi}]"
asm eax, $RESULT
call ADD6
eval "jmp [{edi}]"
asm eax, $RESULT
call ADD6
popa
ret
///////////////////////////
ADD5:
add eax, 05
ret
///////////////////////////
ADD6:
add eax, 06
ret

greetz


Link to comment

I uploaded a new version Scylla_v0.9.4_RC


 


https://forum.tuts4you.com/files/file/576-scylla-imports-reconstruction/


 


Scylla can now scan and fix all direct imports with the "universal" method in the options. It scans: LEA, MOV, PUSH, JMP, CALL. The only disadvantage is that you have to add relocations for DLLs, Scylla generates the necessary relocation information in the log file.


 


This method is really the perfect solution for this problem. It has 100% accuracy (if there are no bugs in the implementation). Scanning for xrefs/guessing bytes is really bad, because you can easily miss something.


Edited by Aguila
Link to comment

Hi,


 


ok I have test your new version quickly and see it does not fix / create pointer addresses = [0]



01001000 >- E9 3CA7807B JMP 7C80B741 ; kernel32.GetModuleHandleA 01001018 7C80AE40 kernel32.GetProcAddress
0100101C 7C80B741 kernel32.GetModuleHandleA
01001020 90909090 get fixed in dumped file too 01001000 - E9 FB2F0100 JMP 01014000 01014000 FF25 00000000 JMP DWORD PTR DS:[0] Log
ImageBase 01000000 ImageSize 00014000 IATAddress 01001018 IATSize 0x8
0001 AddrVA 01001000 Type JMP Value 7C80B741 IatRefPointer 00000000 Api kernel32.dll GetModuleHandleA
------------------------------------------------------------
Relocation direct imports fix: Base RVA 00014000 Offset 0002 Type IMAGE_REL_BASED_HIGHLOW

Can you check this again?


 


greetz


Link to comment

@LCF-AT


Thanks for testing. This is the last bug I have to fix. It is because IatRefPointer 00000000, you mentioned this bug earlier. It cannot find the value in the iat.


Edited by Aguila
Link to comment

Ok I see another problem about the section sizes.So I have create a single direct API command below in the codesection of Notepad.exe at address VA 010087FE | jmp GetModuleHandleA then I start your tool and it does find this 1 direct command but in the dumped & fixed file its no more there = just overwritten with zero bytes and I also have not enabled the option "Use PE Header from disk".So I think here you should add a another check to prevent overwriting real bytes.So in both versions "Universal & Normal" it was happend so and the fixed command with Normal fix was fixed but after dumping with your tool it was no more there.Problem is that your tool still used the original raw size without to update or check this code below.


 


PS: So before I did enter the IAT where the GMHA API was also to find and this case it should not have any problem to fix it with the found pointer.



01014000 JMP DWORD PTR DS:[0] to 01014000 JMP DWORD PTR DS:[0100101C] ; GMHA

Anyway so just try to fix this and the other problems and also add a direct API command fix also if the API xy was not found in the entered IAT so then just add a new import in your scylla section later.So this means all normal or direct found imports get fixed to found or entered IAT if API is there and all others getting a pointer address into your scylla section where your tool added all missing APIs so that you now at the end have fixed the normal IAT [in codesection for exsample]+ extra IAT [stored in Scylla section].So this you can also do in blind show modus same as Universal fix mode where you see the changes just in the dumped file later and not in realtime.


 


greetz


Link to comment

Ok I see another problem about the section sizes.So I have create a single direct API command below in the codesection of Notepad.exe at address VA 010087FE | jmp GetModuleHandleA then I start your tool and it does find this 1 direct command but in the dumped & fixed file its no more there = just overwritten with zero bytes and I also have not enabled the option "Use PE Header from disk".So I think here you should add a another check to prevent overwriting real bytes.So in both versions "Universal & Normal" it was happend so and the fixed command with Normal fix was fixed but after dumping with your tool it was no more there.Problem is that your tool still used the original raw size without to update or check this code below.

You mentioned this dump bug years ago, but I still don't know why this happens.

I dont use the raw size field, I always use the virtual size field. If you set the option "use pe header from disk" the virtualsize of the disk image is used.

 

Can you upload your notepad.exe?

Link to comment

Hi,

During the little adventures with Winlicense x64 (the unpackmes you uploaded), I found that Winlicense uses:

CALL [indirect api] (probably possible to write a tracer for this)

db 00

;stuff

Don't know if that's useful :)

Greetings

Link to comment

Thanks for the information.


 


Winlicense offers 3 different API Wrappers. In the max protection unpackme I chose "Level 3".



 


Advance API-Wrapping


 


This option will enable advanced API-Wrapping techniques that keep an attacker from identifying the different APIs that are used by a protected application. You can select the API-Wrapper level, which will obscure more the process of hiding a specific API. Notice that a higher level will require more CPU processing to boot up your application.



 


When I have time I will look at them a little bit and write some plugin.


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...