Jump to content
Tuts 4 You

Scylla IAT fix functions as DLL/Lib


Recommended Posts

Hey there,


as the available Scylla DLL by Aguila only supports dumping and I needed a good IAT fixing DLL/Lib, I made a wrapper around the Scylla source.

Also because the available ImpRec DLL isnt such as easy to use as I wished.


Check out the source on BitBucket https://bitbucket.org/cypherpunk/scylla_wrapper_dll

or grab attached binaries:

Its based on latest Scylla source. Basically it mimics all steps you do in the GUI version but also offers more detailed control if you need it.



  • IAT AutoSearch
  • reading Imports
  • validating Imports
  • cutting Imports (if the corresponding module would be empty, its cut too)
  • fixing a Dump
  • Dumping
  • PE rebuilder


Test it, report bugs and feature requests and I'll see what I can do. Also PullRequests on BitBucket are welcome !


Pretty easy to use, see below.

What has been changed:
- Native API calls (Nt*) replaced by WinAPI calls
- stripped all WTL/ATL dependencies
- stripped GUI (obviously)
//searches IAT, writes to iatStart, iatSize
int scylla_searchIAT(DWORD pid, DWORD_PTR &iatStart, DWORD &iatSize, DWORD_PTR searchStart, bool advancedSearch);
//reads the imports, iatAddr is VA
int scylla_getImports(DWORD_PTR iatAddr, DWORD iatSize, DWORD pid, LPVOID PVOID invalidImportCallback = NULL);
//add a module manually, in case auto-search didnt get it, e.g scattered IAT
bool scylla_addModule(const WCHAR* moduleName, DWORD_PTR firstThunkRVA);
//add API manually, in case auto-search didnt get it, e.g scattered IAT
bool scylla_addImport(const WCHAR* importName, DWORD_PTR thunkVA);
//are all imports valid?
bool scylla_importsValid();
//cut an Import, because its invalid or whatever reason. Calling this from within the invalidImportCallback will crash!
//Call it after scylla_getImports call returned !
bool scylla_cutImport(DWORD_PTR apiAddr);
//fix the dump
int scylla_fixDump(WCHAR* dumpFile, WCHAR* iatFixFile);
//fix a mapped dump
int scylla_fixMappedDump(DWORD_PTR iatVA, DWORD_PTR FileMapVA, HANDLE hFileMap);
//get imported DLL count
int scylla_getModuleCount();
//get total API Imports count
int scylla_getImportCount();
//enumerate imports tree
void scylla_enumImportTree(LPVOID enumCallBack);
//size which the new IAT will consume
long scylla_estimatedIATSize();
//get thunkVA by API name
DWORD_PTR scylla_findImportWriteLocation(char* importName);
//get thunkVA by ordinal
DWORD_PTR scylla_findOrdinalImportWriteLocation(DWORD_PTR ordinalNumber);
//get API name by thunkVA, cast return to char*
DWORD_PTR scylla_findImportNameByWriteLocation(DWORD_PTR thunkVA);
//get DLL name by thunkVA, cast return to char*
DWORD_PTR scylla_findModuleNameByWriteLocation(DWORD_PTR thunkVA); //dumps a process
bool scylla_dumpProcessW(DWORD_PTR pid, const WCHAR * fileToDump, DWORD_PTR imagebase, DWORD_PTR entrypoint, const WCHAR * fileResult);
bool scylla_dumpProcessA(DWORD_PTR pid, const char * fileToDump, DWORD_PTR imagebase, DWORD_PTR entrypoint, const char * fileResult); //rebuilds a files PE header
bool scylla_rebuildFileW(const WCHAR * fileToRebuild, BOOL removeDosStub, BOOL updatePeHeaderChecksum, BOOL createBackup);
bool scylla_rebuildFileA(const char * fileToRebuild, BOOL removeDosStub, BOOL updatePeHeaderChecksum, BOOL createBackup); Return Codes
typedef int (*SEARCHIAT) (DWORD, DWORD_PTR &, DWORD &, DWORD_PTR, bool);
typedef bool (*IMPORTSVALID) ();
typedef int (*FIXDUMP) (WCHAR*, WCHAR*); HMODULE lib = LoadLibrary(_T("scylla_wrapper"));
SEARCHIAT searchIAT = (SEARCHIAT) GetProcAddress(lib, "scylla_searchIAT");
GETIMPORTS getImports = (GETIMPORTS) GetProcAddress(lib, "scylla_getImports");
IMPORTSVALID importsValid = (IMPORTSVALID) GetProcAddress(lib, "scylla_importsValid");
FIXDUMP fixDump = (FIXDUMP) GetProcAddress(lib, "scylla_fixDump"); DWORD iatStart = 0xDEADBEEF;
DWORD iatSize = 0xDEADBEEF; int search = searchIAT(fdProcessInfo->dwProcessId, iatStart, iatSize, eip, false); if(search==0) int imports = getImports(iatStart, iatSize, pid);
bool valid = importsValid();
if(valid) int fix = fixDump(dumpFileName, IatFixFileName); Definitions
typedef void*(*fCallback)(LPVOID invalidImport); //e.g.
void* cbInvalidImport(void* apiAddr) typedef void(*fCallback)(LPVOID importDetail); typedef struct
bool NewDll;
int NumberOfImports;
ULONG_PTR ImageBase;
ULONG_PTR BaseImportThunk;
ULONG_PTR ImportThunk;
char* APIName;
char* DLLName;
} ImportEnumData //e.g. pointer on this struct used in scylla_enumImportTree as argument
void cbEnumImports(void* importDetail)
ImportEnumData* data = (ImportEnumData*)importDetail;
} Notes
The pre-compiled binaries and project standard uses _cdecl calling convention.
For assembly users, this means you have to push arguments from right-to-left onto the stack
and clean the stack yourself after calling. #pragma pack(push,1) or compiler flag /Zp1 is needed for the struct members to be aligned correctly

Big shouts to Aguila for coding Scylla and making it OpenSource !

Edited by cypher
  • Like 4
Link to comment

improving/increasing the dll functions is a great idea, but I don't like the way you do it.


My advice is:

Use the recent source from here: https://github.com/NtQuery/Scylla/

Check in the complete source without editing the source!

Do your changes, but try to change only the necessary things. Try to keep the original source as complete as possible.


Right now I don't know what you edited and your code is not future proof. What if I change something in scylla? It is really hard to adjust the changes to your code.


Why did you strip the other dll exports? This doesn't make any sense.

Link to comment

Hey Aguila,


I agree with you. Changes to upstream need to be documented better.


The original source is as complete and original as it can be. Only left out files it doesnt need. Changes can be easily copied over. What I edited was

  • add stdafx include in every cpp
  • removed references to Scylla Config and the GUI
  • changed Native calls to WinAPI calls because otherwise when used from within a TitanEngine project, TE runs into an exception

I could add Diff files if that helps?


I didnt strip other DLL exports, I started this from scratch, so I am adding exports to it :) I can and will probably easily add the dumping functions. IAT AutoSearch is already done locally but not pushed up yet.

Link to comment

you dont need to write down or comment your changes. BUT the changes should be visible in the version control system... that is why I said, first commit everything and then begin to change it.


I see that you created a new solution file, but I still wonder why you are doing this. I think it is a bad approach.

Link to comment

here is now the official scylla dll with the 2 new dll functions.


complete list of functions:

BOOL __stdcall ScyllaDumpCurrentProcessW(const WCHAR * fileToDump, DWORD_PTR imagebase, DWORD_PTR entrypoint, const WCHAR * fileResult);
BOOL __stdcall ScyllaDumpCurrentProcessA(const char * fileToDump, DWORD_PTR imagebase, DWORD_PTR entrypoint, const char * fileResult); BOOL __stdcall ScyllaDumpProcessW(DWORD_PTR pid, const WCHAR * fileToDump, DWORD_PTR imagebase, DWORD_PTR entrypoint, const WCHAR * fileResult);
BOOL __stdcall ScyllaDumpProcessA(DWORD_PTR pid, const char * fileToDump, DWORD_PTR imagebase, DWORD_PTR entrypoint, const char * fileResult); BOOL __stdcall ScyllaRebuildFileW(const WCHAR * fileToRebuild, BOOL removeDosStub, BOOL updatePeHeaderChecksum, BOOL createBackup);
BOOL __stdcall ScyllaRebuildFileA(const char * fileToRebuild, BOOL removeDosStub, BOOL updatePeHeaderChecksum, BOOL createBackup); const WCHAR * __stdcall ScyllaVersionInformationW();
const char * __stdcall ScyllaVersionInformationA();
DWORD __stdcall ScyllaVersionInformationDword(); int __stdcall ScyllaStartGui(DWORD dwProcessId, HINSTANCE mod); int __stdcall ScyllaIatSearch(DWORD dwProcessId, DWORD_PTR * iatStart, DWORD * iatSize, DWORD_PTR searchStart, BOOL advancedSearch);
int __stdcall ScyllaIatFixAutoW(DWORD_PTR iatAddr, DWORD iatSize, DWORD dwProcessId, const WCHAR * dumpFile, const WCHAR * iatFixFile);


Link to comment

Updated version in first post.


New features:
- searchIAT
- getImports
- importsValid
- fixDump


Also the original Scylla DLL exports have been added:

- dumpProcessW

- dumpProcessA

- rebuildFileW

- rebuildFileA

Edited by cypher
Link to comment



you can now fix a filemapped dump with fixMappedDump(DWORD_PTR iatVA, DWORD_PTR FileMapVA, HANDLE hFileMap);


You have to map it yourself and it already needs to have enough space somewhere to store the new IAT. Either a new section or somewhere else.

Link to comment



question: So I try this functions now a little with MultiASM plugin just for testing a little.


1.) scylla_searchIAT function.So this will work so far to get the IAT start VA & size of a normal IAT but it will fail if the normal IAT was changed a little so then I get no datas back only in eax the value FC instead of 00 = success.

01001008 >77DCBA55 ADVAPI32.RegCreateKeyW
0100100C >FFFFFFFF <--- modded
01001010 >77DA7ABB ADVAPI32.RegQueryValueExA

The question is why in this case I get no infos back about IAT start / size of the rest you know?So I think a another log parameter would be better to log the VA locations of not valid dwords + more diffrent return parameters in eax would be also a good idea.


So a other problem with this API is if I add a another API normaly at the end of the IAT "or" if I just fill some APIs at the IAT top with 00 dwords and use again this API then I get again the same old results which are of course no more up to date = same IAT start VA and same IAT size.Why?

01001000 >00000000 <-- IAT start I filled just with 00 dwords
01001004 >00000000
01001008 >00000000
0100100C >00000000
01001010 >00000000
01001014 >77DA7852 ADVAPI32.RegOpenKeyExA
01001018 >77DAD767 ADVAPI32.RegSetValueExW
0100101C 00000000
01001020 >773AD270 COMCTL32.CreateStatusWindowW
01001024 00000000
01001028 >77F0DC61 GDI32.EndPage
01001340 >77C1806B msvcrt.wcsncpy
01001344 00000000 <-- Normal IAT end size = 344
01001348 77C1806B msvcrt.wcsncpy <--- I added one more
0100134C 00000000 <--- Next scan should get more size
01001350 00000000

Also it would be a good idea to add a custom function to fix single imports & different IAT blocks aerea's too = smart collect / prepair & fix method instead only to have a static basic method.Just only a idea of course. :)



Link to comment

0xFC = -4 = SCY_ERROR_IATNOTFOUND, meaning scylla wasnt able to locate the IAT, maybe try a different searchStart parameter. For testing, I always let the "real" Scylla do the same searching/reading imports with same OEP like I used as startAddress (oep, codesectionAddr etc). The results should be the same, otherwise theres a bug in the wrapper. If not, either bug or unsupported in Scylla or you are doing sth wonky :P


I cant answer you WHY it doesnt find it as the DLL only proxies to Scylla and this seems to be clearly sth about the search logic in scylla. Maybe Aguila can help.


When searchIAT was successful however, you can use getImports to read the IAT, then call importsValid to see if all are fine. A callback and/or function for investigating/cutting invalid imports will probably be added somewhen in the near future.


For your second example, assuming scylla found the correct size and start for the original IAT, you could simply modify iatsStart = iatStart - 0x340 and also iatSize and then use those modified values for getImports call. This should work as long as you added valid IAT entries...

Edited by cypher
Link to comment

@LCF-AT could you please try the PDB symbol file. If placed at the same place as the dll/lib, Olly should load it automatically and you should have more debug infos. This PDB is for the debug build. Maybe need to rename it to match the DLL name.

If it works we can easily provide PDBs for this wrapper and for TitanEngine.


not working

Edited by cypher
Link to comment


cypher's dll is not really usable in assembler. You should try the iat search function with my dll.


Use the advanced search.


call GetCurrentProcessId

push eax

push offset1

push offset2

push SomeAddressInCodeSection

push 1


dwort ptr ds:[offset1] <- start

dwort ptr ds:[offset2] <- size

Link to comment

@Aguila, would you elaborate that please?

My DLL doesnt do anything different to your DLL. It uses the exact same code. In fact you backported the functions from my DLL to yours, so what is the difference when using either one of the DLLs in assembler ?

Link to comment



- added a callback to getImports which will get called for every import that is invalid

- added export cutImport(DWORD_PTR apiAddr)



  1. searchIAT
  2. getImports -> callback receives invalid API ptr address, return valid addr or 0 if you dont care now and save the addr for later use
  3. importsValid -> false? -> iterate saved API addresses and call deleteAPI
  4. importsValid -> should be true now
  5. fixDump

Note: Dont dont dont call deleteAPI directly from within the callback!! Do that AFTER getImports call returned

Edited by cypher
Link to comment

cdecl calling convention


Thanks for pointing this out. This means:


- You will have to push arguments from right-to-left on the stack before calling

- You will have to clean up the stack yourself after calling

Link to comment

So mate, I've now tried both versions of Scylla - Aguila's original .dll, and yours.

I'm coding this little app in C#, and have of course declared the functions as such.


Now, scylla_searchIAT, scylla_getImports, and scylla_importsValid are all working without any problems - but when it comes to the actual rebuilding,

only error code I get is 254...

Oh, and with the original Scylla .dll by Aguila - the error code is; IATFailed or something...


Any suggestions to what I can do to solve this problem here?

Also, I tried using the GUI function of the original Scylla - and that works flawlessly ;)

Link to comment



I cant help you much here as that return code directly comes from a call to the udnerlying Scylla code by Aguila. So maybe he can help.

Its strange and unlikely however that the GUI does work when input parameters are the same, because the DLLs really mostly forward to the same internal functions.

Link to comment


               uint iatStart = 0;

                uint iatSize = 0;


                ScyllaIAT.scylla_searchIAT(debugger.Process.Id, ref iatStart, ref iatSize, newOEP + debugger.ProcessImageBase, false);

                if (iatSize > 0)


                    SCYLLA_IATFIX_API getImps = ScyllaIAT.scylla_getImports(iatStart, iatSize, debugger.Process.Id);


                    Console.WriteLine("IAT Start: " + iatStart.ToString("X8"));

                    Console.WriteLine("IAT Size: " + iatSize.ToString("X8"));

                    Console.WriteLine("getImports: " + getImps);


                    if (ScyllaIAT.scylla_importsValid())


                        SCYLLA_IATFIX_API check = ScyllaIAT.scylla_fixDump(dumpOpts.OutputPath, dumpOpts.OutputPath + "_fixed.exe", ".txte");

                        Console.WriteLine("fixDump: " + check.ToString());




As I said, everything works except the fixDump or IATFix function...

Link to comment

have you tried tracing/stepping into the debug version of the dll ? Namely into this function

if (importRebuild.rebuildImportTable(fixedFilePath, moduleList)) //trace in here

that way we could better see what exactly fails while trying to fix it: not a valid PE, adding section, or actually writing the IAT into new section

Link to comment

Updated version in first post.


New features:

  • getModuleCount
  • getImportCount
  • enumImportTree
  • estimatedIATSize

x64 builds now available with every new version

Edited by cypher
Link to comment

Updated version in first post


New features:

  • addModule //add a module manually, in case auto-search didnt get it, e.g scattered IAT
  • addImport //add a module manually, in case auto-search didnt get it, e.g scattered IAT
  • findImportWriteLocation //get thunkVA by API name
  • findOrdinalImportWriteLocation //get thunkVA by ordinal
  • findImportNameByWriteLocation //get API name by thunkVA, cast return to char*
  • findModuleNameByWriteLocation //get DLL name by thunkVA, cast return to char*

Thats it folks. No more features planned. Only on request and considered useful. And of course bugfixing!

Edited by cypher
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...